一、安全世界观:为什么前端需要关注安全?常见攻击面与防御原则
1.1 前端安全,真的那么重要吗?
说实话,我早年做前端的时候,也觉得安全是后端的事。
那时候我心想:用户输入的数据最终都要交给后端校验,前端就是个展示层,能有什么风险?
直到有一次,我负责的一个电商项目上线第二天,就被人用 XSS 攻击窃取了用户的登录凭证。那天晚上我盯着日志,看着攻击者模拟用户下单、修改收货地址,后背一阵发凉。
嗯,从那以后,我再也不敢说「前端不需要关注安全」这种话了。
核心认知:前端是用户与系统交互的第一道防线。攻击者往往先从前端下手,因为前端代码对用户完全可见,攻击成本最低。
你想想看,一个用户打开你的网页,浏览器会下载并执行你所有的 HTML、CSS、JavaScript。这些代码完全暴露在攻击者眼皮底下。如果前端代码存在漏洞,攻击者可以直接篡改页面行为、窃取用户数据,甚至控制整个用户会话。
1.2 常见攻击面:攻击者都在打什么主意?
我在项目中遇到过不少安全事件,总结下来,前端常见的攻击面主要有这几类:
| 攻击类型 | 攻击目标 | 常见场景 |
|---|---|---|
| XSS(跨站脚本攻击) | 窃取用户 Cookie、会话令牌 | 评论区、搜索框、富文本编辑器 |
| CSRF(跨站请求伪造) | 冒充用户执行操作 | 转账、修改密码、发表评论 |
| 点击劫持 | 诱导用户点击隐藏按钮 | 钓鱼页面、虚假抽奖 |
| 中间人攻击 | 窃听或篡改通信数据 | 公共 WiFi、HTTP 页面 |
| 前端存储泄露 | 获取 localStorage、sessionStorage 中的敏感信息 | Token 存储不当、明文密码缓存 |
1.3 XSS 攻击:最经典的前端漏洞
XSS 说白了就是攻击者把恶意脚本「注入」到你的页面里。用户访问页面时,脚本就会在用户的浏览器中执行。
我记得有一次做代码审计,发现一个同事写的搜索功能直接把用户输入拼接到 innerHTML 里:
// ❌ 危险写法
document.getElementById('result').innerHTML = '搜索结果:' + userInput;
攻击者只需要输入 <script>alert('xss')</script>,就能在页面上执行任意 JavaScript。更可怕的是,他们可以读取 document.cookie,然后把你的登录凭证发到自己的服务器上。
避坑指南:我曾经接手过一个老项目,整个代码库有 200 多处直接拼接 HTML 的地方。改了一个月才全部修复。所以,从一开始就要养成好习惯——永远不要信任用户输入。
正确的做法是使用 textContent 替代 innerHTML,或者对用户输入进行转义:
// ✅ 安全写法
const safeText = document.createTextNode(userInput);
document.getElementById('result').appendChild(safeText);
// 或者使用现代框架(React、Vue)的模板语法
// 它们默认会对输出进行转义
1.4 CSRF 攻击:你以为是你操作的?
CSRF 攻击有点「借刀杀人」的意思。攻击者诱导用户访问一个恶意页面,这个页面偷偷向你的网站发送请求。因为用户已经登录了你的网站,浏览器会自动带上 Cookie,服务器就以为这是用户本人的操作。
为什么会这样?因为 Cookie 的发送机制是「自动携带」的,服务器无法区分请求是来自用户主动点击,还是来自恶意页面。
防御 CSRF 其实不难,我常用的方法有:
- 使用 SameSite Cookie 属性:设置
SameSite=Strict或SameSite=Lax,限制跨站请求携带 Cookie - 添加 CSRF Token:在表单中嵌入一个随机 Token,服务器验证 Token 是否匹配
- 验证 Referer 头:检查请求来源是否合法
小技巧:我个人习惯在前后端分离的项目中使用 SameSite=Strict + 自定义请求头(如 X-Requested-With: XMLHttpRequest)双重防护。这样即使 SameSite 在某些浏览器上不生效,还有第二道防线。
1.5 点击劫持:看不见的陷阱
点击劫持是一种「视觉欺骗」攻击。攻击者把目标网站用 iframe 嵌入到自己的页面中,然后通过 CSS 把 iframe 设置为透明,再在上面覆盖一个诱人的按钮。
用户以为自己点的是「领取优惠券」,实际上点的是「删除账号」按钮。
防御方法很简单,设置一个 HTTP 响应头就行:
// 在服务器端设置
X-Frame-Options: DENY
// 或者
X-Frame-Options: SAMEORIGIN
这个头告诉浏览器:不允许其他网站用 iframe 加载你的页面。或者只允许同源网站加载。
1.6 防御原则:建立安全思维
说了这么多攻击方式,其实防御的核心原则就那么几条。我总结了一个「安全三原则」,每次做项目都会对照检查:
- 最小权限原则:只给代码和用户最小的必要权限。比如,不需要操作 Cookie 的脚本就不要给它访问 Cookie 的能力。
- 纵深防御原则:不要依赖单层防护。前端做校验,后端也要做校验。即使前端被攻破,后端还能挡住。
- 永不信任用户输入原则:所有来自用户的数据都是「脏数据」,必须经过验证、过滤、转义才能使用。
我的经验:每次写代码前,先问自己三个问题——「这段代码会处理用户输入吗?」「用户输入可能包含恶意内容吗?」「如果被攻击了,损失有多大?」。养成这个习惯,能避免 80% 的安全问题。
1.7 安全不是一次性的工作
说实话,安全防护不是「写完就完事」的。攻击手段在进化,浏览器的安全策略也在更新。
我建议团队把安全检查纳入日常开发流程:
- 代码审查时,专门检查安全相关的代码
- 使用自动化工具扫描依赖库的漏洞
- 定期进行安全测试,模拟攻击场景
- 关注浏览器的安全更新和新特性
你想想看,一个安全漏洞从发现到被利用,可能只需要几个小时。而修复一个漏洞,可能只需要几分钟。但如果你不知道漏洞的存在,那后果就严重了。
嗯,这一章我们先建立安全世界观。后面的章节,我会带着大家逐个攻破具体的漏洞类型,从原理到实战,一步步把前端安全防线建起来。
课后思考:打开你正在开发的项目,检查一下有没有直接拼接用户输入到 HTML 中的代码。如果有,想想怎么改。这是最基础也是最有效的安全实践。