6.前端安全之 XSS 与 CSRF
来源:https://w0hog67yl81.feishu.cn/wiki/CHsVwiqBGifpmLkPcdtcXjDincb
采集状态:已完成正文采集;每次约0.55屏连续滚动到文末,按block id和代码行号累积;代码行号连续。
核心思想
前端安全的核心在于“信任”问题。我们永远不能信任任何来自用户(或第三方)的输入。XSS 和 CSRF 是两种最常见、也最具代表性的 Web 攻击方式,它们都巧妙地利用了网站对用户或浏览器行为的“信任”。
一、XSS (Cross-Site Scripting) 跨站脚本攻击
1. 攻击原理
攻击者将恶意的 JavaScript 脚本注入到网页中,当其他用户访问该网页时,这些恶意脚本就会在用户的浏览器中执行,从而窃取用户信息(如 Cookie)、操作用户行为、或在页面上展示恶意内容。
本质: 恶意代码在受害者的浏览器上“无中生有”地执行了。
2. 攻防演练:一个简单的评论区
【攻】攻击演示
假设一个网站的评论区,后端直接将用户提交的评论内容插入到页面的 HTML 中。
易受攻击的代码 (前端渲染)
// 从后端获取评论列表
const comments = [
{ author: '小明', text: '这是一条正常的评论。' },
{ author: 'Hacker', text: '' }
];
// 前端直接将内容渲染到页面
const commentContainer = document.getElementById('comment-list');
comments.forEach(comment => {
// 危险操作:直接使用 innerHTML 渲染未经过处理的用户输入
commentContainer.innerHTML += `
${comment.author}: ${comment.text}
`;
});
攻击效果
当任何用户浏览这个评论区时:
1.
浏览器在渲染 Hacker 的评论时,会遇到 这段字符串本身。
策略二:内容安全策略 (Content Security Policy, CSP) - 深度防御
CSP 通过设置 HTTP 头部,告知浏览器一个“白名单”,规定页面只允许加载和执行来自特定来源的脚本。即使攻击者成功注入了脚本,由于脚本来源不在白名单内,浏览器也会拒绝执行。
示例 HTTP 头部
Content-Security-Policy: script-src 'self' https://trusted.cdn.com;
这个策略告诉浏览器:只允许执行来自当前域名('self')和 https://trusted.cdn.com 的脚本。所有内联脚本(inline script)和来自其他域的脚本都将被阻止。
二、CSRF (Cross-Site Request Forgery) 跨站请求伪造
1. 攻击原理
攻击者诱导一个已经登录的受害者,在不知情的情况下,从一个第三方恶意网站向被攻击网站发送一个伪造的请求(如转账、修改密码等)。由于这个请求是受害者浏览器发出的,会自动带上被攻击网站的 Cookie,因此服务器会误以为这是用户的真实意图并执行。
本质: 攻击者冒用你的身份,发送了一个你并非本意的请求。请求是合法的,但并非出自你的意愿。
2. 攻防演练:一个简单的转账操作
【攻】攻击演示
假设 bank.com 有一个通过 GET 请求就能完成转账的功能。
易受攻击的 API (bank.com)
// GET /transfer?to=Hacker&amount=1000
// 只要用户登录了 bank.com,访问这个 URL 就会立刻转账。
// 服务器仅通过 Cookie 判断用户身份。
恶意网站的代码 (hacker.com)
攻击者在自己的网站上放置一个看不见的图片标签。
劲爆!点击查看!
攻击效果
1.
用户登录了 bank.com,Cookie 已保存在浏览器中。
2.
用户被诱导访问了 hacker.com 的恶意页面。
3.
浏览器看到
标签,会立即尝试加载其 src 指向的 URL,也就是向 bank.com 发起一个 GET 请求。
4.
根据同源策略,浏览器在发送这个跨站请求时,会自动带上 bank.com 域下的 Cookie。
5.
bank.com 的服务器收到请求,验证 Cookie 发现是合法用户,于是执行转账操作。用户在毫不知情的情况下损失了 1000 元。
【防】防御策略
核心防御思路是:增加一个攻击者无法伪造的、与用户会话绑定的“凭证”。
策略一:Anti-CSRF Token (同步令牌模式) - 主要防御手段
1.
当用户访问表单页面时,服务器生成一个随机的、唯一的 token,并将其嵌入到表单的隐藏字段中。同时,服务器在 session 中也保存这个 token。
2.
当用户提交表单时,这个 token 会随表单一起发送到服务器。
3.
服务器比较表单提交的 token 和 session 中保存的 token。如果两者一致,则为合法请求;如果不一致,则拒绝请求。
修复后的防御代码 (以 Node.js/Express 为例)
1. 渲染表单时,加入 token
// GET /show_transfer_form
app.get('/transfer', (req, res) => {
// 生成 token 并存入 session
req.session.csrfToken = generateRandomToken();
res.render('transfer_form', { csrfToken: req.session.csrfToken });
});
2. HTML 表单中嵌入 token
3. 处理请求时,验证 token
// POST /transfer
app.post('/transfer', (req, res) => {
if (req.body._csrf !== req.session.csrfToken) {
return res.status(403).send('Invalid CSRF Token');
}
// Token 验证通过,执行转账逻辑...
// ...
res.send('转账成功!');
});
防御效果
攻击者在 hacker.com 无法得知 bank.com 在用户 session 中生成的 csrfToken 是什么,因此他们无法伪造一个带有正确 _csrf 值的请求。当伪造请求到达服务器时,会因为 token 验证失败而被拒绝。
策略二:SameSite Cookie 属性 - 釜底抽薪
通过在设置 Cookie 时增加 SameSite 属性,可以告诉浏览器在跨站请求中是否要携带这个 Cookie。
Set-Cookie: session_id=...; HttpOnly; SameSite=Strict
•
SameSite=Strict: 最严格。任何跨站请求(包括从收藏夹打开、地址栏输入)都不会携带 Cookie。CSRF 攻击完全失效。
•
SameSite=Lax: 较为宽松。允许在一些安全的顶级导航 GET 请求中携带 Cookie(如点击链接跳转),但对于 POST、
、