一、前言
前端安全问题往往被忽视,但它与后端安全同样重要。随着 Web 应用的复杂化,攻击者不再只是针对服务器,更通过浏览器、接口、脚本注入等方式直接攻击用户。
本篇文章将带你从常见漏洞入手,结合 复现 + 修复 示例,系统掌握前端安全防护要点。
二、常见安全问题分类
| 类型 | 描述 | 危害 |
|---|---|---|
| XSS(跨站脚本攻击) | 恶意脚本注入页面执行 | 窃取Cookie、Token、伪造操作 |
| CSRF(跨站请求伪造) | 利用用户登录状态发起恶意请求 | 伪造转账、改密码等操作 |
| 点击劫持 | 在透明 iframe 上覆盖按钮 | 诱导用户误点 |
| DOM Clobbering | 通过篡改 DOM 对象污染全局 | 导致逻辑被篡改或RCE |
| URL 重定向 | 未验证的跳转参数 | 钓鱼、劫持 |
| 本地存储泄露 | localStorage / sessionStorage 数据泄露 | 暴露 Token、用户信息 |
| 依赖包供应链攻击 | 恶意 npm 包篡改或注入 | 执行恶意代码、后门 |
三、XSS(跨站脚本攻击)
📍1. 攻击原理
攻击者将恶意脚本注入页面中,使浏览器执行攻击代码。
📍2. 漏洞示例
<!-- demo.html -->
<div id="msg"></div>
<script>
const query = location.search.slice(1); // ?msg=<script>alert(1)</script>
document.getElementById('msg').innerHTML = query;
</script>
访问
http://localhost/demo.html?msg=<script>alert('XSS')</script>
💥 页面直接弹出弹窗,XSS 攻击成功。
✅3. 修复方式
✅ 方式一:转义输出内容
function escapeHTML(str) {
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """);
}
document.getElementById('msg').innerHTML = escapeHTML(query);
✅ 方式二:使用安全的 DOM API
document.getElementById('msg').textContent = query;
✅ 方式三:开启 CSP(内容安全策略)
在 HTTP 响应头中加入:
Content-Security-Policy: default-src 'self'; script-src 'self'
👉 阻止外部脚本加载与内联脚本执行。
四、CSRF(跨站请求伪造)
📍1. 攻击原理
攻击者诱导登录用户访问伪造链接,使其在登录状态下自动发出恶意请求。
📍2. 漏洞示例
假设银行转账接口:
// POST /api/transfer?to=attacker&amount=1000
fetch("/api/transfer?to=attacker&amount=1000", { method: "POST" });
攻击者在钓鱼页面中放入:
<img src="https://bank.com/api/transfer?to=attacker&amount=1000">
用户若已登录银行账户,请求将直接被触发。
✅3. 防范手段
✅ 方式一:CSRF Token
服务器为每次请求生成唯一 Token,前端随请求头发送:
fetch("/api/transfer", {
method: "POST",
headers: { "X-CSRF-Token": localStorage.getItem('csrfToken') },
body: JSON.stringify({ to: "userB", amount: 100 })
});
✅ 方式二:SameSite Cookie
设置 Cookie:
Set-Cookie: sessionId=xxx; SameSite=Strict
👉 限制第三方页面自动携带 Cookie。
✅ 方式三:Referer / Origin 验证
服务端检查请求来源:
if (!['https://mybank.com'].includes(req.headers.origin)) {
return res.status(403).end();
}
五、点击劫持(Clickjacking)
📍1. 攻击原理
攻击者在 iframe 中嵌套你的页面并覆盖透明按钮,让用户误操作。
📍2. 防御方案
✅ 方式一:禁止被 iframe 嵌套
HTTP 响应头:
X-Frame-Options: DENY
✅ 方式二:使用 CSP
Content-Security-Policy: frame-ancestors 'none';
✅ 方式三:JS 自检
if (window.top !== window.self) {
window.top.location = window.self.location;
}
六、DOM Clobbering
📍1. 示例攻击
<form name="login">
<input name="action" value="evil()">
</form>
<script>
// 被污染
console.log(login.action); // <input ...>
login.action(); // ❌ 本意是函数调用,却被替换成DOM对象
</script>
✅2. 防御
- 避免使用
id、name作为全局变量访问 - 使用
document.getElementById()明确取值 - 避免在 DOM 中信任动态创建的节点结构
七、本地存储安全(Token、缓存数据)
📍1. 问题
localStorage 可被任何脚本读取,一旦页面存在 XSS,即 Token 泄露。
✅2. 建议
- 不要存储敏感信息(Token、密码)
- Token 建议保存在 HttpOnly Cookie
- 若必须使用 localStorage,结合 加密 + 短期有效期
// AES 加密示例(CryptoJS)
import CryptoJS from "crypto-js";
const secret = "app_secret_key";
const data = "user_token";
const encrypted = CryptoJS.AES.encrypt(data, secret).toString();
localStorage.setItem("tk", encrypted);
// 解密
const bytes = CryptoJS.AES.decrypt(localStorage.getItem("tk"), secret);
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
八、依赖供应链安全
📍1. 攻击方式
攻击者通过恶意 npm 包或版本污染实现攻击:
require('lodash'); // 被替换为恶意代码
✅2. 防御建议
- 使用 npm audit 或 pnpm audit 检查依赖漏洞
- 锁定版本:
package-lock.json/pnpm-lock.yaml - 启用 CI 自动检测工具:
九、安全编码最佳实践
✅ 输出前统一转义
✅ 严格 CSP 策略
✅ 静态资源版本锁定
✅ HTTPS + Secure Cookie
✅ Webpack 构建阶段移除 console 与 eval
✅ 接口请求统一封装验证来源与 Token
🔐 十、项目实战落地方案
可以在前端项目中引入统一的安全中间层模块,例如 src/utils/security.ts:
// src/utils/security.ts
export const Security = {
escapeHTML(str: string) {
return str.replace(/[&<>"']/g, s => ({
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
}[s]!));
},
sanitizeURL(url: string) {
const safe = /^https?:\/\//.test(url);
return safe ? url : 'about:blank';
},
csp() {
const meta = document.createElement('meta');
meta.httpEquiv = 'Content-Security-Policy';
meta.content = "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'";
document.head.appendChild(meta);
}
};
// 项目入口调用
Security.csp();
🧠 结语
前端安全并非“后端的责任”,它贯穿开发、构建、部署的整个生命周期。
掌握本文提到的 攻击原理 + 防御手段 + 代码实操,
你就能有效避免 90% 的常见安全漏洞。
💬 “安全不是成本,而是产品的信任基石。”
文章评论