引言

跨站脚本攻击(Cross-Site Scripting,简称 XSS)是 Web 安全中最常见、影响最广泛的漏洞之一。它允许攻击者将恶意脚本注入到目标页面,在用户浏览器中执行,从而窃取凭证、劫持会话、篡改页面内容,并可进一步发动钓鱼、勒索或蠕虫传播。

XSS 基础概念

XSS 的三类常见类型

  1. 反射型 XSS(Reflected XSS)

    • 恶意脚本作为请求参数提交给服务器,服务器将其原样返回并嵌入页面
    • 通常通过钓鱼链接触发
  2. 存储型 XSS(Stored XSS)

    • 恶意脚本存储在数据库、评论区、日志等持久位置
    • 所有访问该内容的用户都会触发
  3. DOM型 XSS(DOM-based XSS)

    • 漏洞发生在客户端脚本处理 DOM 时,攻击者控制的数据直接写入页面
    • 与服务器返回内容无关,仅由前端脚本引发

XSS 的攻击目标

  • Cookie / session:窃取登录状态
  • 本地存储 / 会话存储:窃取敏感数据
  • 页面 DOM:篡改页面、植入钓鱼界面
  • 浏览器行为:重定向用户、发起隐蔽请求
  • 浏览器扩展接口:在少数情况下获取扩展数据

XSS 的利用原理

反射型 XSS 示例

1
2
3
4
5
6
7
8
9
10
11
<!-- 伪造搜索结果页面 -->
<form action="/search" method="get">
<input name="q" value="">
<button type="submit">搜索</button>
</form>

<p>搜索结果:
<script>
document.write('您搜索的是:' + location.search.split('q=')[1]);
</script>
</p>

若输入<script>alert(1)</script>并返回未转义,则页面会执行该脚本。

存储型 XSS 示例

1
2
3
4
5
<!-- 评论发布接口直接保存输入 -->
<form action="/comment" method="post">
<textarea name="comment"></textarea>
<button type="submit">提交</button>
</form>

如果后端将comment字段原样输出到页面,则所有浏览用户都会执行该脚本。

DOM XSS 示例

1
2
3
4
5
<div id="output"></div>
<script>
var hash = location.hash.substring(1);
document.getElementById('output').innerHTML = hash;
</script>

访问http://site.com/page#<img src=x onerror=alert(1)>即可触发 DOM XSS。

现代 XSS 变体

DOM Mutation XSS

当前端代码使用innerHTMLouterHTMLinsertAdjacentHTMLdocument.write等,将攻击者控制的数据插入页面时,即可触发。

属性型 XSS

攻击者将恶意输入写入属性值,如hrefsrcstyleonerror等。

1
<img src="x" onerror="fetch('/steal?c=' + document.cookie)">

JavaScript 语句注入

不安全地构造eval()new Function()setTimeout(string)时,用户输入可直接被当成代码执行。

1
2
var userCode = getUserInput();
eval('var a = "' + userCode + '";');

存储型 JSON / API XSS

当应用将用户生成内容存储为 JSON 并由前端模板反序列化时,若未正确转义也可能被利用。

XSS 常见 payload 和攻击技巧

基本 payload

  • <script>alert(1)</script>
  • '><script>alert(1)</script>
  • "><img src=x onerror=alert(1)>
  • javascript:alert(1)

进阶 payload

  • </script><svg/onload=alert(1)>
  • <iframe srcdoc="<script>alert(1)</script>">
  • <details open ontoggle=alert(1)>(利用未知标签属性)
  • <body onload=alert(1)>(少数浏览器可用)

HTML 属性与事件处理器绕过

  • onerror=alert(1)
  • onmouseover=alert(1)
  • style="background:url(javascript:alert(1))"

JavaScript URL 绕过

  • <a href="javascript:alert(1)">click</a>
  • <img src="x" onerror="location='javascript:alert(1)'"></img>

DOM XSS 典型 payload

1
#hash=<svg/onload=alert(1)>

1
#fragment=<img src=x onerror=alert(1)>

这些 payload 适用于前端直接将 hash/fragment 输出到页面的场景。

XSS 利用后果

会话窃取

通过document.cookie读取Cookie并发送给攻击者。

页面劫持

修改按钮、表单、链接,重定向用户到钓鱼页面。

持久化后门

在管理员页面插入恶意脚本,影响后续所有用户。

横向传播

在社交平台、论坛中自动发布恶意链接,实现蠕虫式传播。

检测与审计

手工测试

  1. 查找输入点:搜索框、评论、URL 参数、cookie、HTTP 头
  2. 注入易触发 payload:<script>alert(1)</script><img src=x onerror=alert(1)>
  3. 检查是否存在未转义输出

工具支持

  • Burp Suite:Intruder、Scanner
  • OWASP ZAP
  • DOM Invader
  • XSSer
  • Nuclei、Arachni

代码审计要点

  • 查找输出点:innerHTMLouterHTMLdocument.writeevalsetTimeoutFunction
  • 查找服务器端输出:echo, print, jsp:out, Response.Write
  • 查找模板渲染:是否对变量进行了转义

浏览器安全检测

  • 检查 CSP 是否启用
  • 观察 XSS Filter / 浏览器拦截行为

XSS 防御策略

防御核心:输入过滤 + 输出编码

对输出进行上下文转义

不同输出上下文使用不同编码:

  • HTML body:转义<, >, &, ", '
  • HTML 属性:还需转义/=、空格
  • JavaScript:转义引号、换行、
  • URL:使用encodeURIComponent
  • CSS:转义特殊字符

使用安全模板引擎

  • React / Vue / Angular:默认对插值进行转义
  • Mustache、Handlebars:原生模板变量自动转义
  • 不要使用innerHTMLv-html渲染用户输入,除非经过严格清洗

采用 Content Security Policy (CSP)

CSP 可以降低 XSS 风险,特别是通过禁用内联脚本和未经授权的资源加载。

1
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';

对关键 Cookie 添加HttpOnlySecure标记,防止被脚本读取。

避免危险 DOM API

  • 避免innerHTML, outerHTML, insertAdjacentHTML, document.write
  • 使用textContentsetAttributecreateTextNode等安全 API

前端框架安全使用

  • React:避免dangerouslySetInnerHTML
  • Vue:避免v-html
  • Angular:避免[innerHTML]绑定未经信任数据

输入验证与白名单

对可控数据进行类型检查和白名单校验,如只允许数字、枚举值、特定 URL 模式等。

框架与语言防御示例

PHP

1
2
3
4
5
function escape_html($value) {
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

echo escape_html($user_input);

Java

1
2
String safe = ESAPI.encoder().encodeForHTML(userInput);
out.print(safe);

JavaScript

1
2
const node = document.createTextNode(userInput);
document.getElementById('output').appendChild(node);

Python

1
2
from markupsafe import escape
print(escape(user_input))

XSS 高级防御

安全默认值

默认禁止所有不可信数据写入页面,只有经过清洗的数据才能渲染。

多层防御

  • 服务器端输出编码
  • 前端安全渲染
  • CSP 作为防线
  • HttpOnly / Secure Cookie

最小权限原则

限制 JavaScript 能访问的接口,例如使用Content-Security-Policy: script-src 'self'frame-ancestors 'none'

监控与告警

  • 记录未转义输出点
  • 遇到异常输入时报警
  • 检测异常脚本加载来源

典型案例与攻击链

案例 1:社交平台存储型 XSS

攻击者在评论区注入<script>fetch('/steal?c='+document.cookie)</script>,当管理员访问该评论时,Cookie 被窃取并发送给攻击者,导致平台后台账号被控制。

案例 2:搜索结果反射型 XSS

搜索页面将q参数直接写入结果区域。攻击者将 payload 伪装成搜索链接发送给受害者,触发脚本并窃取登录状态或劫持会话。

案例 3:单页应用 DOM XSS

前端应用从location.hash读取参数并直接写入页面。即使后端数据安全,攻击者仍可通过 URL 片段执行脚本。

案例 4:CSP 绕过与内联脚本

某应用仅设置Content-Security-Policy: default-src 'self',但允许unsafe-inline。攻击者借助内联脚本或javascript: URL 绕过防护。

现代 Web 环境中的 XSS 重点

AJAX 与 JSON

JSON 接口如果将用户输入插入 HTML 片段,同样会导致 XSS。前端不得将未转义 JSON 数据用于 DOM 插入。

Web Component / Shadow DOM

自定义组件中仍需对传入属性和槽内容进行严格转义。Shadow DOM 仅隔离样式,不阻止 XSS。

依赖库风险

第三方库、富文本编辑器、Markdown 渲染器都可能引入 XSS。必须对输入进行沙箱过滤或使用可信安全配置。

总结

XSS 的本质是:不可信输入进入了能够执行脚本的上下文。只要保持“输入不可信、输出必须编码”的原则,并结合安全渲染框架、CSP 和浏览器保护,就能构建稳固的防御体系。

  • 反射型用于钓鱼链接,存储型用于持久化攻击,DOM 型则是前端实现缺陷
  • 所有输出上下文都需要上下文敏感转义
  • 现代框架默认安全并非绝对,危险 API 仍需避免
  • 多层次防御比单一措施更可靠

通过掌握 XSS 攻击原理和防御细节,开发人员可有效降低 Web 应用遭受脚本注入的风险。