Appearance
目录
核心思想
前端安全的核心在于"信任"问题。我们永远不能信任任何来自用户(或第三方)的输入。XSS 和 CSRF 是两种最常见、也最具代表性的 Web 攻击方式,它们都巧妙地利用了网站对用户或浏览器行为的"信任"。
一、XSS (Cross-Site Scripting) 跨站脚本攻击
1. 攻击原理
攻击者将恶意的 JavaScript 脚本注入到网页中,当其他用户访问该网页时,这些恶意脚本就会在用户的浏览器中执行,从而窃取用户信息(如 Cookie)、操作用户行为、或在页面上展示恶意内容。
本质: 恶意代码在受害者的浏览器上"无中生有"地执行了。
2. 攻防演练:一个简单的评论区
【攻】攻击演示
假设一个网站的评论区,后端直接将用户提交的评论内容插入到页面的 HTML 中。
易受攻击的代码 (前端渲染)
javascript
// 从后端获取评论列表
const comments = [
{ author: '小明', text: '这是一条正常的评论。' },
{
author: 'Hacker',
text: '<script>alert("XSS Attack! Your cookie is: " + document.cookie)</script>'
}
]
// 前端直接将内容渲染到页面
const commentContainer = document.getElementById('comment-list')
comments.forEach(comment => {
// 危险操作:直接使用 innerHTML 渲染未经过处理的用户输入
commentContainer.innerHTML += `<div><strong>${comment.author}:</strong> ${comment.text}</div>`
})攻击效果
当任何用户浏览这个评论区时:
- 浏览器在渲染 Hacker 的评论时,会遇到
<script>标签。 - 浏览器会立即执行其中的 JavaScript 代码。
- 页面会弹出一个警告框,显示当前用户的 Cookie。攻击者可以将 alert 换成更复杂的代码,将 Cookie 发送到自己的服务器,从而盗用用户身份。
【防】防御策略
核心防御思路是:"数据"永远是"数据",绝不能让它变成"代码"。
策略一:HTML 转义 - 主要防御手段
在将用户输入的内容输出到页面时,对具有特殊含义的 HTML 字符(如 <, >, &, ", ')进行转义。
修复后的防御代码
javascript
function escapeHTML(str) {
if (!str) return ''
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
}
// ... 获取评论列表的代码 ...
comments.forEach(comment => {
// 安全的做法:先转义,再渲染
const safeText = escapeHTML(comment.text)
// 或者使用 textContent 属性,它会自动处理转义
const div = document.createElement('div')
const strong = document.createElement('strong')
strong.textContent = comment.author + ': '
div.appendChild(strong)
div.appendChild(document.createTextNode(safeText)) // 使用 createTextNode 也是绝对安全的
commentContainer.appendChild(div)
})防御效果
经过转义后,Hacker 的恶意评论在 HTML 源码中会变成:
html
<div>
<strong>Hacker:</strong> <script>alert("XSS Attack! Your cookie is: " +
document.cookie)</script>
</div>浏览器只会将这段代码作为纯文本显示在页面上,而不会执行它。用户看到的就是<script>alert(...)</script>这段字符串本身。
策略二:内容安全策略 - 深度防御
CSP 通过设置 HTTP 头部,告知浏览器一个"白名单",规定页面只允许加载和执行来自特定来源的脚本。即使攻击者成功注入了脚本,由于脚本来源不在白名单内,浏览器也会拒绝执行。
示例 HTTP 头部
Content-Security-Policy: script-src 'self' https://trusted.cdn.com;这个策略告诉浏览器:只允许执行来自当前域名和 https://trusted.cdn.com 的脚本。所有内联脚本和来自其他域的脚本都将被阻止。
二、CSRF (Cross-Site Request Forgery) 跨站请求伪造
1. 攻击原理
攻击者诱导一个已经登录的受害者,在不知情的情况下,从一个第三方恶意网站向被攻击网站发送一个伪造的请求(如转账、修改密码等)。由于这个请求是受害者浏览器发出的,会自动带上被攻击网站的 Cookie,因此服务器会误以为这是用户的真实意图并执行。
本质: 攻击者冒用你的身份,发送了一个你并非本意的请求。请求是合法的,但并非出自你的意愿。
2. 攻防演练:一个简单的转账操作
【攻】攻击演示
假设 bank.com 有一个通过 GET 请求就能完成转账的功能。
易受攻击的 API (bank.com)
javascript
// GET /transfer?to=Hacker&amount=1000
// 只要用户登录了 bank.com,访问这个 URL 就会立刻转账。
// 服务器仅通过 Cookie 判断用户身份。恶意网站的代码
攻击者在自己的网站上放置一个看不见的图片标签。
html
<!-- hacker.com/malicious_page.html -->
<h1>劲爆!点击查看!</h1>
<!-- 用户看不到这个图片,但浏览器会尝试加载它 -->
<img
src="http://bank.com/transfer?to=Hacker&amount=1000"
width="1"
height="1"
style="display:none;"
/>攻击效果
- 用户登录了 bank.com,Cookie 已保存在浏览器中。
- 用户被诱导访问了 hacker.com 的恶意页面。
- 浏览器看到
<img>标签,会立即尝试加载其 src 指向的 URL,也就是向 bank.com 发起一个 GET 请求。 - 根据同源策略,浏览器在发送这个跨站请求时,会自动带上 bank.com 域下的 Cookie。
- bank.com 的服务器收到请求,验证 Cookie 发现是合法用户,于是执行转账操作。用户在毫不知情的情况下损失了 1000 元。
【防】防御策略
核心防御思路是:增加一个攻击者无法伪造的、与用户会话绑定的"凭证"。
策略一:Anti-CSRF Token (同步令牌模式) - 主要防御手段
- 当用户访问表单页面时,服务器生成一个随机的、唯一的 token,并将其嵌入到表单的隐藏字段中。同时,服务器在 session 中也保存这个 token。
- 当用户提交表单时,这个 token 会随表单一起发送到服务器。
- 服务器比较表单提交的 token 和 session 中保存的 token。如果两者一致,则为合法请求;如果不一致,则拒绝请求。
修复后的防御代码 (以 Node.js/Express 为例)
- 渲染表单时,加入 token
javascript
// GET /show_transfer_form
app.get('/transfer', (req, res) => {
// 生成 token 并存入 session
req.session.csrfToken = generateRandomToken()
res.render('transfer_form', { csrfToken: req.session.csrfToken })
})- HTML 表单中嵌入 token
html
<form action="/transfer" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>" />
<input type="text" name="to" placeholder="收款人" />
<input type="text" name="amount" placeholder="金额" />
<button type="submit">转账</button>
</form>- 处理请求时,验证 token
javascript
// 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=StrictSameSite=Strict: 最严格。任何跨站请求(包括从收藏夹打开、地址栏输入)都不会携带 Cookie。CSRF 攻击完全失效。SameSite=Lax: 较为宽松。允许在一些安全的顶级导航 GET 请求中携带 Cookie(如点击链接跳转),但对于 POST、<img>、<iframe>等高风险的跨站请求,则不会发送 Cookie。这也能有效防御绝大多数 CSRF 攻击。
这是目前浏览器层面最简单、最有效的 CSRF 防御手段。
总结对比
| 特征 | XSS (跨站脚本) | CSRF (跨站请求伪造) |
|---|---|---|
| 攻击目标 | 窃取用户在客户端的信息和权限 | 冒用用户身份,执行服务端的操作 |
| 信任利用 | 网站信任了用户输入的内容 | 网站信任了用户的浏览器(的Cookie) |
| 代码执行地 | 受害者浏览器中 | 攻击者无法注入或执行代码 |
| 核心防御 | 输入过滤和输出转义,不让数据变代码 | Token 或 SameSite Cookie,验证请求来源 |