XSS漏洞

XSS(跨站脚本攻击) 是一种网络安全漏洞,攻击者利用它将恶意的脚本代码“注入”到其他用户信任的网页中。当受害者的浏览器执行这些恶意代码时,攻击者就可以盗取用户信息、冒充用户身份进行操作等。XSS分为DOM型XSS、反射型XSS、存储型XSS。

1.DOM型XSS

1.1 什么是DOM

DOM(文档对象模型)是HTML和XML文档的编程接口。它将一个文档表示成一个由节点和对象组成的树形结构,允许编程语言(如JavaScript)动态地访问和更新文档的内容、结构和样式。

当浏览器加载一个网页时,它会解析HTML并创建这个DOM树。之后,JavaScript可以通过DOM提供的方法(如document.getElementByIdelement.innerHTML等)来修改这个树,从而改变显示在用户面前的页面。

1.2 DOM的树形结构

假设我们有一个这样简单的HTML:

<!DOCTYPE html>
<html>
<head>
    <title>我的页面</title>
</head>
<body>
    <h1>我的标题</h1>
    <a href="https://example.com">我的链接</a>
</body>
</html>

浏览器会为它构建如下所示的 DOM 树:

文档 (Document)
└── HTML根元素 (<html>)
    ├── 头部 (<head>)
    │   └── 标题 (<title>)
    │       └── 文本节点 ("我的页面")
    └── 主体 (<body>)
        ├── 一级标题 (<h1>)
        │   └── 文本节点 ("我的标题")
        └── 链接 (<a href="https://example.com">)
            └── 文本节点 ("我的链接")

在这个树中,每个标签(如 <html><body><h1>)都是一个元素节点,标签内的文字是文本节点,而属性(如 href="https://example.com")是属性节点

1.3 DOM的作用

DOM的核心作用是让JavaScript程序能够与网页内容进行交互。没有DOM,网页就是静态的、无法改变的。有了DOM,我们才能创造出丰富的动态交互体验。

具体来说,通过DOM,JavaScript可以:

(1)动态改变内容。你可以选中一个段落,然后改变它的文字。

document.querySelector(‘p’).textContent = ‘这段文字被JS修改了!’;

(2)动态改变样式。你可以让一个按钮在点击时改变颜色。

document.getElementById(‘myButton’).style.backgroundColor = ‘blue’;

(3)对用户交互做出反应。你可以为按钮、链接、输入框等元素绑定“点击”、“鼠标悬停”、“按键”等事件。

document.getElementById(‘myButton’).addEventListener(‘click’, function() {
  alert(‘按钮被点击了!’);
});

(4)动态地创建、添加和删除元素。你可以根据用户的操作,在列表中动态添加新的项目,或者删除不需要的项目。

let newItem = document.createElement(‘li’); // 创建一个新的<li>元素
newItem.textContent = ‘新列表项’; // 设置它的内容
document.querySelector(‘ul’).appendChild(newItem); // 把它添加到<ul>中

1.4 DOM型XSS攻击

DOM型XSS攻击是一种发生在受害者浏览器中的攻击,攻击者通过操纵页面的文档对象模型来注入并执行恶意代码。整个攻击过程可以完全在客户端完成,服务器的响应(初始的HTML)可能是“干净”的、完全没有问题的,问题出在页面本身的JavaScript逻辑上。

攻击举例:

low难度

这是DVWA靶场中没有针对DOM型XSS攻击任何防护的网站。

查看前端JavaScript代码是有缺陷的,代码判断当前页面URL是否包含"default="参数,如果存在,从URL中提取"default="后面的值作为语言代码创建一个选中该语言的选项。也就是说可以注入一些 恶意的JS 代码进去,然后这部分会被包含到 lang 变量中,最终回显到页面上。

if (document.location.href.indexOf("default=") >= 0) {
    var lang = document.location.href.substring(document.location.href.indexOf("default=") + 8);
    document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
    document.write("<option value='' disabled='disabled'>----</option>");
}

document.write("<option value='English'>English</option>");
document.write("<option value='French'>French</option>");
document.write("<option value='Spanish'>Spanish</option>");
document.write("<option value='German'>German</option>");

HTML DOM 有个 alert() 方法,用于显示带有一条指定消息和一个 OK 按钮的警告框。

document.cookie 里面可以读到 cookie 信息,把 cookie 放在一个 alert() 生成的警告框中,回显时就会得到我们想要的信息了。注入的payload 如下:

<script>alert(document.cookie)</script>

medium难度

前端代码不变,后端代码如下所示。stripos(string,find,start) 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写),header() 函数向客户端发送原始的 HTTP 报头。也就是说现在服务器通过一个模式匹配,过滤了 “script” 标签,不能直接注入 JS 代码了。

可以利用标签闭合 + 事件处理器绕过过滤。HTML 的 < img > 标签定义 HTML 页面中的图像,该标签支持 onerror 事件,在装载文档或图像的过程中如果发生了错误就会触发从而输出cookie。构造payload如下:

</option></select><img src = 1 onerror = alert(document.cookie)>

high难度

前端代码不变,后端代码如下所示。服务器设置了白名单,default 参数只接受 French,English,German 以及 Spanish 这几个单词。

可以在注入的 payload 中加入注释符 “#”,注释后边的内容不会发送到服务端,但是会被前端代码所执行。在 URL 中,# 被称为片段标识符。它的作用是告诉浏览器:“这个锚点后面的内容,是给前端(浏览器)自己看的,不需要发给服务器。构造payload如下:

English #<script>alert(document.cookie)</script>

2.反射型XSS

2.1 反射型XSS攻击

放射型 XSS(Reflected XSS) 是一种跨站脚本攻击(Cross-Site Scripting, XSS)类型,指攻击者将恶意脚本代码作为参数注入到用户的请求中,并通过服务器原样返回这些参数到网页中,导致脚本被立即执行。

攻击举例:

low难度

这是DVWA靶场中没有针对反射型XSS攻击任何防护的网站。

通常浏览器中设置了XSS防护,这里将其关闭来方便演示漏洞。后端代码只是判断了name参数是否为空,如果不为空的话就直接打印出来,并没有对name参数做任何的过滤和检查,没用进行任何的对XSS攻击的防御措施,存在非常明显的XSS漏洞,用户输入什么都会被执行。

用alert进行弹窗测试验证是否存在XSS,注入payload如下:

<script>alert(document.cookie)</script>

medium难度

后端代码检查GET请求中是否存在"name"参数,并且该参数的值不为NULL。从GET请求中获取"name"参数的值,并尝试移除其中出现的所有"<script>"字符串。注意,这里只移除了完整的"<script>",但不会移除大小写不同的变体(如"<SCRIPT>")或者嵌套在其他字符串中的情况,也不会处理其他形式的XSS向量(比如使用其他标签或事件处理器)。

这里通过大写<SCRIPT>的方式来绕过后端检测,注入payload如下:

<SCRIPT>alert(document.cookie)</SCRIPT>

high难度

正则表达式使用了i修饰符(不区分大小写),所以大小写混合无法绕过,但是因为正则表达式匹配的是<script,所以如果使用其他标签则不受影响。

使用img标签来绕过,注入payload如下:

​​​​<img src=x onerror=alert(document.cookie)>

3.存储型XSS

3.1 存储型XSS攻击

存储型XSS是指恶意脚本被永久存储在目标服务器的数据库、文件或其他存储介质中,当其他用户访问包含这些恶意内容的页面时,脚本会自动执行。

攻击举例:

low难度

这段代码是一个简单的留言板功能,用户可以在其中输入姓名和留言,然后将其存储到数据库中。在低安全级别下,代码没有对用户输入进行充分的过滤,导致存储型XSS成为可能。

<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = stripslashes( $message );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitize name input
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}
?>

用alert进行弹窗测试验证是否存在XSS,当上传留言后,页面会显示留言的内容,会自动自行恶意脚本,注入payload如下:

<script>alert(document.cookie)</script>

先进入HOME页,再回到XSS(Stored)页面,仍然可以得到cookie,证明存储型XSS攻击成功。

如果需要删除数据库中存在的XSS代码,进入dvwa数据库中guestbook表,删除恶意脚本。

medium难度

后端代码对留言内容做了严格处理:

      addslashes():在特殊字符前添加反斜线(防SQL注入)

  • strip_tags():移除HTML、XML和PHP标签

  • mysqli_real_escape_string():转义SQL特殊字符

  • htmlspecialchars():将特殊字符转换为HTML实体

但是对名称这里只移除了完整的"<script>",但不会移除大小写不同的变体(如"<SCRIPT>")或者嵌套在其他字符串中的情况,也不会处理其他形式的XSS向量(比如使用其他标签或事件处理器)。


<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = str_replace( '<script>', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}
?>

这里通过大写<SCRIPT>的方式来绕过后端检测,因为前端代码对名称有长度限制,所以需要burp suite该参数,注入payload如下:

<SCRIPT>alert(document.cookie)</SCRIPT>

先进入HOME页,再回到XSS(Stored)页面,仍然可以得到cookie,证明存储型XSS攻击成功。

如果需要删除数据库中存在的XSS代码,进入dvwa数据库中guestbook表,删除恶意脚本。

high难度

后端代码对留言内容的防护和中等难度的一样;对名称的检测使用了正则表达式,正则表达式使用了i修饰符(不区分大小写),所以大小写混合无法绕过,但是因为正则表达式匹配的是<script,所以如果使用其他标签则不受影响。

<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}
?>

 使用img标签来绕过,因为前端代码对名称有长度限制,所以需要burp suite该参数,注入payload如下:

​​​​<img src=1 onerror=alert(document.cookie)>

先进入HOME页,再回到XSS(Stored)页面,仍然可以得到cookie,证明存储型XSS攻击成功。

如果需要删除数据库中存在的XSS代码,进入dvwa数据库中guestbook表,删除恶意脚本。

4.区别特征

特征反射型XSSDOM型XSS存储型XSS
核心原理恶意输入由服务器"反射"回HTML页面恶意输入由客户端JavaScript写入DOM恶意输入被永久存储在服务器,然后返回给所有用户
是否经过服务器是,恶意载荷是HTTP请求的一部分不一定,恶意载荷可能只在URL片段中,不会发送到服务器是,恶意载荷会发送到服务器并被永久存储
执行原因服务器返回的HTML中包含了恶意脚本,浏览器将其作为页面的一部分解析客户端JS不安全地操作了DOM服务器从存储中读取恶意脚本并返回,浏览器将其作为页面的一部分解析
数据流客户端 → 服务器 → 客户端客户端 → (客户端)客户端(攻击者) → 服务器(存储) → 客户端(所有受害者)
漏洞位置服务器端代码(输出未转义)客户端JavaScript代码(逻辑不安全)服务器端代码(存储和输出都未正确处理)
持久性非持久(一次性)可能持久,可能非持久持久(永久存储在服务器)
触发条件用户点击特定恶意链接用户访问包含漏洞JS的页面用户访问正常的受感染页面

5.XSS防御

核心在于永远不要信任用户的输入,并对所有不可信的数据进行严格的验证和适当的处理。

防御层面核心措施具体做法与说明
输入处理输入验证与过滤白名单验证:只接受符合预期格式和范围的数据(如只允许数字、特定格式的邮箱)。
过滤敏感字符:移除或转义 <>"'& 等可能用于构造脚本的字符。
输出处理输出编码HTML编码:使用 htmlspecialchars() 等函数,将数据作为纯文本安全插入HTML。关键点:根据数据将要放入的上下文(HTML、JavaScript、URL等)选择合适的编码方式。
代码编写使用安全的DOM操作方法避免 innerHTML/outerHTML:这些属性会解析字符串中的HTML标签,非常危险。
使用安全属性:优先使用 textContent 或 innerText来设置纯文本内容。
使用DOM方法:通过 document.createElement()setAttribute() 和 appendChild() 等API来安全地创建和操作元素。
整体防护实施内容安全策略 (CSP)

限制脚本源:通过HTTP头Content-Security-Policy告诉浏览器只执行来自特定可信来源的脚本,可以有效阻止内联脚本和恶意外部脚本的执行。

-HttpOnly:防止JS读取敏感Cookie

### 定义 XSS(Cross-Site Scripting)即跨站脚本攻击,是一种常见的 Web 安全漏洞,攻击者通过在目标网站注入恶意脚本,当其他用户访问该网站时,恶意脚本会在用户的浏览器中执行,从而窃取用户信息或进行其他恶意操作[^1]。 ### 原理 - **反射型**:攻击者构造包含恶意脚本的 URL,诱导用户点击,服务器接收到请求后将恶意脚本反射到响应中,浏览器解析并执行该脚本。 - **存储型**:攻击者提交一段 XSS 代码后,服务器接收并存储,当其他用户访问包含该 XSS 代码的页面时,XSS 代码被浏览器解析并执行。允许用户存储数据到服务器端的 Web 应用程序可能存在该类型 XSS 漏洞[^2]。 - **DOM 型**:基于 DOM(文档对象模型)的操作,通过修改页面的 DOM 结构来注入恶意脚本,不涉及服务器端数据的存储和反射。 ### 检测 - **常见 POC**:POC(Proof of Concept)即概念验证,常指一段漏洞证明的代码。例如,在反射型 XSS 检测中,可以构造包含简单 JavaScript 代码的 URL,如 `http://example.com/search?keyword=<script>alert('XSS')</script>`,如果页面弹出提示框,则说明可能存在反射型 XSS 漏洞[^3]。 - **自动化工具**:使用专业的安全检测工具,如 OWASP ZAP、Burp Suite 等,这些工具可以自动扫描网站,检测潜在的 XSS 漏洞。 ### 修复 - **输入过滤**:对用户输入进行严格的过滤,只允许合法的字符和格式。例如,使用正则表达式过滤特殊字符,防止恶意脚本注入。 - **输出编码**:在将用户输入输出到页面时,进行 HTML 实体编码,将特殊字符转换为 HTML 实体,防止浏览器将其解析为脚本。例如,将 `<` 转换为 `<`,`>` 转换为 `>`。 ```java import org.owasp.esapi.ESAPI; public class XSSUtils { public static String encodeForHTML(String input) { return ESAPI.encoder().encodeForHTML(input); } } ``` - **标签黑白名单过滤**:设置标签黑白名单,只允许或禁止特定的 HTML 标签和属性。例如,只允许 `<b>`、`<i>` 等简单标签,禁止 `<script>` 标签。 - **HttpOnly 属性**:对于存储用户敏感信息的 Cookie,设置 HttpOnly 属性,防止 JavaScript 脚本通过 `document.cookie` 窃取 Cookie 信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值