Web应用安全 · kp-013
跨站脚本 XSS:类型与防御
一句话定义
跨站脚本(XSS)是把不可信数据送入 HTML 语境后被浏览器当作脚本执行的漏洞,分为存储型、反射型与 DOM 型三类;根治方法是按输出语境做编码,并以 CSP 作为纵深防线。
为什么重要
XSS 一旦成立,攻击者的脚本就以受害者的身份在页面内运行:可发起任意请求(连带瓦解 CSRF 防御)、读取页面数据、引导输入敏感信息。它与 kp-012 同根因(数据越过语境边界成为代码),但防御手段完全不同——SQL 用参数化一招根治,XSS 要按"输出到哪个语境"分别处理,这是它更易出错的原因。
前置知识
kp-011(Web 攻击面):理解浏览器执行 HTML/JS 的机制与同源策略。
核心概念
- 三类分型:存储型(恶意数据持久化后被其他用户浏览时触发,如评论字段)、反射型(恶意数据经 URL 即时回显)、DOM 型(漏洞完全在前端 JS,不可信数据经 innerHTML/document.write 进入 DOM)。
- 输出语境:HTML 体、HTML 属性、JavaScript 字符串、URL、CSS——每个语境的转义规则不同,用错语境的编码等于没编码。
- 前端安全 API:优先 textContent/createTextNode 而非 innerHTML;确需富文本时用白名单净化库(如 DOMPurify 类方案)。
- CSP(Content-Security-Policy):用白名单声明允许的脚本来源,内联脚本需 nonce/hash;即使编码遗漏,CSP 也能阻断未授权脚本执行,是纵深防御层。
- Cookie 防护:HttpOnly 使 XSS 无法直接读会话 Cookie(kp-015),但不阻止脚本以用户身份发请求。
公式、模型或图示
数据流:不可信输入 ──▶ 存储/URL ──▶ 模板渲染 ──▶ HTML/JS 语境 ──▶ 浏览器执行
└──────── 语境边界(此处必须编码)────────┘
HTML 体: < > &
属性: 引号包裹 + 属性编码
JS 字符串: \xHH 十六进制转义(或改用 JSON 序列化 + 安全插入)原理与机制
防御原则可归纳为"默认转义、按语境选择规则、把 JS 当作另一个语境":现代模板引擎(服务端与前端框架)默认对插值做 HTML 转义,事故通常来自三类显式绕过——原始 HTML 插值接口、自拼字符串模板、把用户数据嵌入 script 标签。框架的转义只覆盖 HTML 体语境,属性需引号包裹,JS 语境需独立处理。CSP 的机制是把"哪些脚本能跑"从隐式(页面里有什么就跑什么)改为显式白名单:脚本只允许来自声明的源,内联执行要求 nonce 或 hash,从而在编码遗漏时仍拦住执行。需要强调 CSP 是纵深层:策略配置错误(如同时允许 unsafe-inline 与外部域)会使其形同虚设,不能替代编码本身。
实例或案例
某站评论功能把用户输入直接存库并在页面原样输出,一条包含脚本标签的评论让所有浏览者会话可被劫持(存储型)。修复路径完整链:模板默认转义消除根因 → 富文本需求改用净化白名单 → 上线 CSP 报告模式灰度观察 → 强制模式 + HttpOnly Cookie 兜底。
直观类比
输出编码像海关申报:数据从"文本世界"入境"HTML 世界"时,必须换成本国文字(转义实体)申报,否则被海关(浏览器解析器)当成行李(代码)放行。不同入境口岸(语境)有不同的申报表,用错表格等于没申报。
常见误区
- 黑名单过滤标签=防御:HTML 语境与事件属性、编码变换的组合使黑名单遗漏概率极高,转义才是默认正确。
- "转义一次全场景通用":HTML 转义的数据放进 JS 字符串或 URL 语境照样出问题。
- "HTTPS 能防 XSS":XSS 是应用层问题,与传输加密无关(呼应 kp-011 误区)。
与其他知识点的关系
- kp-012(SQL 注入):同根因不同语境,对照学习效果最佳。
- kp-014(CSRF):XSS 可在页面内直接发起请求,绕过 CSRF 令牌防御。
- kp-016(OWASP Top 10 与安全响应头):CSP 等响应头的完整清单。
自测题
- 三类 XSS 的区别与共同根因是什么?要点:存储/反射/DOM 按数据到达路径分型;共同根因是不可信数据未经语境化处理进入执行语境。
- 为什么 HttpOnly 不能完全防御 XSS?要点:它只保护 Cookie 不被读取;脚本仍可代表用户发请求、改 DOM、钓鱼。
- CSP 在防御体系中的位置是什么?要点:纵深防线——限制脚本执行来源,在编码遗漏时阻断执行,不能替代输出编码。
延伸阅读
《The Tangled Web》(Michal Zalewski):浏览器解析与语境安全的机制级讲解。