跨站请求伪造怎么防护?

wen 网络安全 83

跨站请求伪造怎么防护?深度解析与实操指南

📖 目录导读

  1. 什么是跨站请求伪造(CSRF)?

    核心原理与攻击流程

    跨站请求伪造怎么防护?

  2. CSRF攻击的危害有多大?

    真实场景案例

  3. 为什么你的网站容易被CSRF攻击?

    技术漏洞分析

  4. 主流防护方案详解

    Token机制、Referer验证、SameSite Cookie、自定义请求头

  5. 代码实战:如何实现CSRF防护?

    前后端合作示例(Python/JavaScript)

  6. 常见问题问答

    开发者最关心的10个问题

  7. 总结与最佳实践

什么是跨站请求伪造(CSRF)?

跨站请求伪造(Cross-Site Request Forgery,简称CSRF)是一种利用用户已登录身份,在用户不知情的情况下,向目标网站发起恶意请求的攻击方式。

攻击流程图解

  1. 用户登录A银行网站,获得有效Cookie
  2. 用户未退出,访问了攻击者精心构造的恶意页面B
  3. 页面B自动请求A银行的转账接口(如 bank.com/transfer?to=hacker&amount=10000
  4. 由于浏览器自动携带Cookie,服务器认为该请求合法,转账成功

核心要点:CSRF攻击不窃取数据,而是伪造操作(如修改密码、转账、发帖等)。


CSRF攻击的危害有多大?

根据2024年OWASP Top 10报告,CSRF仍属于关键安全风险,常见危害包括:

  • 金融损失:自动转账、修改支付账户
  • 账户劫持:修改邮箱、密码重置
  • 权限滥用:管理员账户被CURD操作
  • 数据污染:伪造评论、投票、点赞

真实案例

2018年某国内社交平台因CSRF漏洞,导致数百万用户被强迫自动关注黑产账号,造成大量虚假流量。


为什么你的网站容易被CSRF攻击?

根本原因在于 HTTP协议的无状态性浏览器的同源策略不完善

  • 浏览器在发送请求时会自动携带Cookie(包括Session Cookie)
  • 服务器无法区分请求是用户主动发起,还是恶意页面自动触发
  • 许多开发者过度依赖Cookie验证,忽略了请求来源校验

主流防护方案详解

1 同步Token模式(最推荐)

原理:服务器生成一个随机Token,嵌入到表单或请求头中,提交时校验。

实现方式

# 服务端生成Token
csrf_token = secrets.token_hex(16)
session['csrf_token'] = csrf_token
# 返回给前端
return render_template('form.html', token=csrf_token)

前端提交

<form action="/submit" method="POST">
  <input type="hidden" name="_csrf_token" value="{{ token }}">
  <button>提交</button>
</form>

优点:防御效果好,几乎所有框架都支持
缺点:需要维护Token状态,对多页/单页应用略有差异

2 同源检测(Referer/Origin检查)

原理:检查HTTP请求头中的RefererOrigin字段是否来自可信来源。

def check_referer(request):
    allowed_domains = ['example.com', 'sub.example.com']
    ref = request.headers.get('Referer', '')
    if not ref:
        return False
    from urllib.parse import urlparse
    domain = urlparse(ref).netloc
    return domain in allowed_domains

注意:Referer可能被浏览器禁用或伪造,因此建议作为辅助方案。

3 SameSite Cookie属性

原理:设置Cookie的SameSite属性,限制第三方站点发送Cookie。

Set-Cookie: session=abc; SameSite=Strict; HttpOnly; Secure

三种模式

  • Strict:完全禁止第三方请求携带Cookie
  • Lax:允许GET等安全请求携带(默认值)
  • None:允许所有第三方请求(需配合Secure)

适用场景:现代浏览器(Chrome 80+、Firefox 60+)默认Lax模式,可有效防御大部分CSRF攻击。

4 自定义请求头验证

原理:要求所有敏感请求都必须包含自定义HTTP头(如X-Requested-With: XMLHttpRequest)。

实现

  • 前端使用AJAX/ fetch必须设置该头
  • 服务端校验该头是否存在

优点:避免Token存储问题
缺点:仅适用于API请求,且需要确保前端规范


代码实战:前后端协作防护

Flask + Vue 完整示例

后端(Python/Flask)

from flask import Flask, session, request, jsonify
import secrets
app = Flask(__name__)
app.secret_key = 'your-secret-key'
# 生成Token
@app.route('/api/get-csrf-token')
def get_csrf_token():
    token = secrets.token_hex(32)
    session['csrf_token'] = token
    return jsonify({'csrf_token': token})
# 受保护接口
@app.route('/api/transfer', methods=['POST'])
def transfer():
    # 获取请求头的Token
    received_token = request.headers.get('X-CSRF-Token')
    if not received_token or received_token != session.get('csrf_token'):
        return jsonify({'error': 'CSRF验证失败'}), 403
    # 业务逻辑...
    return jsonify({'success': True})

前端(Vue.js + axios)

// 初始化时获取Token
const csrfToken = await axios.get('/api/get-csrf-token')
// 每次请求携带Token
axios.defaults.headers.common['X-CSRF-Token'] = csrfToken.data.csrf_token
// 发送敏感请求
axios.post('/api/transfer', { amount: 1000, to: 'friend' })

常见问题问答

Q1:CSRF和XSS有什么区别?
A:CSRF是伪造请求,利用用户的身份;XSS是注入脚本,直接窃取数据或执行操作,两者经常组合出现,但防护方法不同。

Q2:RESTful API如何防护CSRF?
A:推荐使用 Token放在请求头 + SameSite Cookie 组合方案,对于移动端,还需加入idempotency key(幂等键)。

Q3:SameSite=Strict会不会影响用户体验?
A:会,例如用户从博客点击链接跳转至支付页面,Strict模式下不会携带Cookie,导致需要重新登录,建议使用 Lax模式 平衡安全与体验。

Q4:WebSocket需要防CSRF吗?
A:需要!WebSocket握手时会发送Cookie,建议在握手请求中携带Token验证,或者使用Origin检查。

Q5:Token放在URL参数里安全吗?
A:不安全,URL会记录在浏览器历史、服务器日志中,容易泄露,务必放在请求体请求头中。

Q6:如何防止Token被暴力破解?
A:使用cryptographically secure伪随机数生成器(如Python的secrets模块),长度建议32字节以上,并定期更换。

Q7:多服务架构下如何共享CSRF Token?
A:使用集中式Token存储(如Redis),所有微服务统一从Token服务验证。

Q8:CSRF和CORS有关系吗?
A:CORS控制跨域读取,CSRF攻击的是跨域写操作,即便CORS配置严格,依然可能遭受CSRF攻击。

Q9:GraphQL如何防护CSRF?
A:在GraphQL请求头中加入Token,同时使用查询白名单成本分析限制恶意操作。

Q10:移动端APP需要防CSRF吗?
A:如果APP使用的是原生的HTTP客户端(如OkHttp),盗用Cookie的概率较低,但如使用WebView内嵌网页,则需完全遵循Web防护方案。


总结与最佳实践

核心防护层次

  1. 第一层:使用 SameSite=LaxStrict 的Cookie策略(强制启用)
  2. 第二层:对所有写操作(POST、PUT、DELETE)添加 CSRF Token
  3. 第三层:验证 Referer/Origin 作为补充
  4. 终极保险:关键操作(转账、修改密码)要求输入 二次验证码(如短信验证码)

开发者自检清单

  • [ ] 是否所有状态改变请求都使用Token或自定义头?
  • [ ] Cookie是否设置了HttpOnlySameSiteSecure
  • [ ] 是否对第三方引用资源(图片、付费API)进行了来源限制?
  • [ ] 是否记录了CSRF攻击日志并触发告警?

技术选型建议

框架 推荐防护方式
Django 内置{% csrf_token %}模板标签
Laravel @csrf 指令与 VerifyCsrfToken 中间件
Spring Boot csrf().enable() 与 RequestDataValueProcessor
Express csurf 中间件
Nuxt/Next.js Nuxt Security Module / csurf 插件

最后提醒:CSRF防护不是一次性配置,而是一个持续改进的过程,建议定期进行安全审计,使用自动化工具(如OWASP ZAP)扫描漏洞,并结合业务场景动态调整防护策略。

一句话总结:让每个敏感请求都带上服务器知晓的“暗号”,而不是仅依靠浏览器自动带来的“身份证”。


综合OWASP官方文档、MDN Web文档及主流框架安全实践,经去伪存精整理而成。*

抱歉,评论功能暂时关闭!