Skip to content

XSS 和 CSRF

在 Web 安全领域,XSS(跨站脚本攻击)CSRF(跨站请求伪造) 是最古老、最常见也是面试中必考的两种攻击方式, 虽然它们名字很像,但它们的攻击目标、原理和防御手段截然不同

1. XSS (Cross-Site Scripting) 跨站脚本攻击

核心本质:攻击者想方设法将恶意的 JavaScript 代码注入到你的网站页面中,并让受害者的浏览器执行。

攻击目标:客户端(用户的浏览器)。

一句话理解“你的网站执行了别人的代码。”

1.1 XSS 的三种主要类型

类型原理攻击路径示例
存储型 (Stored)恶意脚本被永久存储在目标服务器(数据库、文件系统)中。危害最大。1. 攻击者在网站的评论区输入 <script>窃取Cookie代码</script> 并提交。
2. 服务器将评论存入数据库。
3. 所有访问该评论页面的用户,浏览器都会执行这段恶意代码。
反射型 (Reflected)恶意脚本作为请求参数(如 URL)发送给服务器,服务器未做处理直接将脚本“反射”回 HTML 页面。1. 攻击者构造恶意链接 http://test.com/search?q=<script>...</script> 并发送给受害者。
2. 受害者点击链接。
3. 服务器返回包含恶意脚本的搜索结果页面。
4. 脚本在受害者浏览器执行。
DOM 型 (DOM-based)纯粹的前端漏洞。前端 JS 代码在处理 URL 参数、Hash 或其他输入时,未经转义直接操作了 DOM。1. 网址带有恶意参数 http://test.com/#<script>...</script>
2. 页面上的 JS 代码读取了 Hash 值并直接用 innerHTML 插入到页面。
3. 脚本执行。整个过程恶意代码不经过服务器

1.2 XSS 的危害

  • 窃取 Cookie 和 Session ID:利用 document.cookie 获取后发送到攻击者服务器,直接盗用用户账号。
  • 页面篡改/钓鱼:修改页面内容,弹出一个假的登录框骗取密码。
  • 键盘记录:监听用户的按键操作。
  • 发起 CSRF 攻击:利用窃取到的凭证,在后台悄悄执行敏感操作。

1.3 XSS 的防御手段 (防线)

  • 防线一:输入过滤/转义 (Sanitization / Escaping)
    • 永远不要相信用户的输入。 任何从客户端提交到服务器,或者从 URL 读取并展示到页面的数据,都必须进行转义(例如将 < 转义为 &lt;> 转义为 &gt;)。可以使用如 sanitize-html 这样的库。
  • 防线二:设置 HttpOnly Cookie
    • 在服务器端设置 Cookie 时加上 HttpOnly 标志。这会让浏览器禁止 JavaScript 通过 document.cookie 读取该 Cookie。即使 XSS 攻击成功,黑客也偷不到关键的身份令牌。
  • 防线三:内容安全策略 (CSP, Content Security Policy)
    • 通过 HTTP 响应头配置,建立一个白名单,告诉浏览器“只允许加载和执行来自特定域名的脚本”。即使黑客注入了脚本,浏览器也会拒绝执行。

2. CSRF (Cross-Site Request Forgery) 跨站请求伪造

核心本质:攻击者利用受害者在目标网站已登录的身份(浏览器自动携带 Cookie 的机制),诱导受害者在不知情的情况下,对目标网站发送恶意请求。

攻击目标:服务端(利用服务端的信任机制)。

一句话理解“你代替黑客,向服务器发出了指令。”

2.1 CSRF 攻击的工作流程

  1. 用户登录了银行网站 bank.com,浏览器保存了包含身份信息的 Cookie。
  2. 用户在没有登出银行网站的情况下,被诱导访问了黑客的恶意网站 hacker.com
  3. 黑客的网站里隐藏了一段代码,例如一个不可见的图片标签: <img src="http://bank.com/transfer?to=hacker&money=10000" />
  4. 用户的浏览器会自动解析这个 <img> 标签,并向 bank.com 发起一个 GET 请求。
  5. 最关键的一步:因为用户之前登录过 bank.com,浏览器在发起这个请求时,会自动、默认地带上 bank.com 的 Cookie
  6. 银行服务器收到请求,验证 Cookie 发现是合法用户,于是执行了转账操作。

2.2 CSRF 攻击成功的三个条件

  1. 目标网站存在可以通过 API 执行的敏感操作(如转账、修改密码)。
  2. 受害者刚刚登录过目标网站,且浏览器中的 Cookie 未过期
  3. 受害者在未登出的状态下,访问了攻击者精心构造的恶意页面。

2.3 CSRF 的防御手段

  • 防线一:同源检测 (Referer Check / Origin Check)
    • 服务器在收到请求时,检查 HTTP 头部的 RefererOrigin 字段。如果发现请求是从 hacker.com 发过来的,而不是从 bank.com 自家的页面发过来的,就直接拒绝请求。
  • 防线二:CSRF Token 验证 (最主流防线)
    • 原理:既然浏览器会自动携带 Cookie,那我们就要求请求中必须携带一个浏览器不会自动发送的东西。
    • 做法:用户打开页面时,服务器生成一个随机的 CSRF Token(存放在 HTML 隐藏字段或由 JS 提取)。当用户发起敏感请求(如表单提交、Ajax)时,必须将这个 Token 作为参数或 Request Header 发送给服务器。因为黑客的网站无法跨域读取到这个 Token,伪造的请求就会被服务器拒绝。
  • 防线三:Cookie 的 SameSite 属性
    • 现代浏览器的神器。为 Cookie 设置 SameSite=StrictLax。这会告诉浏览器:“只有在当前网页和 Cookie 所属域名完全一致(同站)时,才允许携带此 Cookie;如果是从其他网站(跨站)发起的请求,绝对不允许携带”。这从根本上切断了 CSRF 的命脉。

3. XSS vs CSRF 深度对比及常见问题 (FAQ)

3.1 核心区别对照表

对比维度XSS (跨站脚本)CSRF (跨站请求伪造)
攻击方式向页面注入并执行恶意 JS 脚本。借用用户身份,伪造请求发送给服务器。
攻击目标用户的浏览器。服务器。
是否需要登录不需要(任何人访问受污染页面都会中招)。必须(依赖受害者已登录的有效 Cookie)。
对用户凭证的操作试图窃取用户的 Cookie/Token 送给黑客。并不窃取,只是利用浏览器自动带 Cookie 的机制。黑客全程不到 Cookie 的内容。
防御核心思路永远不信任用户输入(过滤、转义、限制脚本)。确认请求的来源和意图(Token 校验、SameSite)。

3.2 为什么用了 JWT (Token) 就不需要防范 CSRF 了?

  • 这是一个常见误区,答案取决于你的 Token 存在哪里
    • 如果你的 JWT 存在 Cookie 中,你依然会受到 CSRF 攻击,因为浏览器还是会自动携带它。
    • 只有当你把 JWT 存放在 localStorage/sessionStorage 中,并在发请求时使用 JS 手动将其添加到 HTTP Header(如 Authorization: Bearer <token>)中时,才天然免疫 CSRF。因为黑客的伪造请求(如 <img> 或跨域 <form> 提交)无法触发你的前端 JS 代码去读取 localStorage 并塞入 Header。

3.3 CSRF Token 可以防止 XSS 攻击吗?

  • 绝对不能,甚至会被 XSS 秒杀。
    • 如果你的网站存在 XSS 漏洞,黑客注入的恶意脚本可以直接读取你页面上的 CSRF Token(无论是写在 DOM 里还是存在全局变量里),然后带着这个合法的 Token 发起 CSRF 请求。
    • 结论:XSS 漏洞的危险级别远高于 CSRF,因为一旦发生 XSS,所有的 CSRF 防御措施(除 SameSite 且不允许 JS 读取的 Cookie 外)都将形同虚设

3.4 设置了 HttpOnly 就能完全防止 XSS 吗?

  • 不能完全防止,只能减轻危害。
    • HttpOnly 只能防止通过 JS 读取 Cookie(防盗号)。但 XSS 的危害不仅限于偷 Cookie。
    • 如果发生了 XSS,黑客依然可以通过执行 JS 脚本来操纵 DOM(例如修改收款账号)、发起 Ajax 请求执行任意操作(此时浏览器仍会自动带上 HttpOnly 的 Cookie 发给服务器)。所以,防范 XSS 的根本还是输入输出的过滤和转义。