Python中的CSRF攻击与CSRF令牌绕过

2023-04-17 00:00:00 令牌 攻击 绕过

CSRF(Cross-Site Request Forgery)攻击是一种常见的网络攻击方式,攻击者通过伪造用户的身份向目标网站发送请求,以完成某种意外操作,从而达到窃取用户信息或推广垃圾信息等目的。而CSRF令牌是一种预防CSRF攻击的重要手段,目的是确保请求的有效性。

在Python中,我们可以通过如下代码实现CSRF令牌机制:

from flask import Flask, render_template_string, session, request
import random, string

app = Flask(__name__)
app.secret_key = 'my_secret_key'

def generate_csrf_token():
    if '_csrf_token' not in session:
        session['_csrf_token'] = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
    return session['_csrf_token']

@app.before_request
def csrf_protect():
    if request.method == 'POST':
        csrf_token = session.pop('_csrf_token', None)
        if not csrf_token or csrf_token != request.form.get('_csrf_token'):
            return render_template_string('Invalid CSRF token')

@app.route('/')
def index():
    csrf_token = generate_csrf_token()
    return render_template_string('''
        <form method="post" action="/">
            <input type="hidden" name="_csrf_token" value="{{ csrf_token }}">
            <input type="text" name="username" placeholder="Username"><br>
            <input type="password" name="password" placeholder="Password"><br>
            <button type="submit">Login</button>
        </form>
    ''', csrf_token=csrf_token)

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

在上述代码中,我们通过Flask框架实现了CSRF令牌机制,主要包括以下几个步骤:

  1. 定义生成CSRF令牌的函数generate_csrf_token,如果不存在令牌则生成一个32位的随机字符串并存储到session中,最后返回令牌;
  2. 在before_request钩子函数中,判断是否为POST请求,如果是,则获取表单中的CSRF令牌值并与session中存储的令牌值进行比对,如果不一致则说明令牌无效;
  3. 在index路由函数中,调用generate_csrf_token函数生成令牌并传入模板中,最终将令牌作为隐藏字段添加到表单中。

通过以上步骤,我们实现了CSRF令牌机制,用户在提交表单时必须携带与session中存储的令牌匹配的CSRF令牌方可通过验证。

而CSRF攻击的绕过则通常通过以下几种方式:

  1. 利用GET请求:CSRF攻击主要针对POST请求,因为GET请求可以直接在URL中携带参数,而POST请求需要通过表单提交数据。所以,攻击者可能会通过构造伪装链接等方式,利用GET请求进行攻击。
  2. 利用COOKIES:通常情况下,COOKIES是用于存储用户信息的,而CSRF攻击也是利用了COOKIES来伪造用户身份。但是,如果攻击者拥有COOKIES信息,也就意味着他已经黑了用户账号,这种情况下也就可以无限制地发起攻击。
  3. 利用Referer设置:Referer是HTTP头部的一个字段,用于标识请求来源URL。在一些网站中,可以通过Referer字段来限制CSRF攻击。攻击者可以通过在请求中设置合适的Referer值来绕过该限制。

为了更好地说明CSRF攻击和绕过,我们可以参考以下代码示例:

from flask import Flask, render_template_string, session, request, redirect
import random, string

app = Flask(__name__)
app.secret_key = 'my_secret_key'

def generate_csrf_token():
    if '_csrf_token' not in session:
        session['_csrf_token'] = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
    return session['_csrf_token']

@app.before_request
def csrf_protect():
    if request.method == 'POST':
        csrf_token = session.pop('_csrf_token', None)
        if not csrf_token or csrf_token != request.form.get('_csrf_token'):
            return render_template_string('Invalid CSRF token')

@app.route('/')
def index():
    csrf_token = generate_csrf_token()
    return render_template_string('''
        <form method="post" action="/">
            <input type="hidden" name="_csrf_token" value="{{ csrf_token }}">
            <input type="text" name="username" placeholder="Username"><br>
            <input type="password" name="password" placeholder="Password"><br>
            <button type="submit">Login</button>
        </form>
    ''', csrf_token=csrf_token)

@app.route('/profile')
def profile():
    if session.get('logged_in'):
        return 'Welcome ' + session['username']
    else:
        return redirect('/')

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    session['username'] = username
    session['logged_in'] = True
    return redirect('/profile')

@app.route('/logout')
def logout():
    session.pop('username', None)
    session.pop('logged_in', None)
    return redirect('/')

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

在上述示例中,我们实现了一个简单的登录系统,通过用户名和密码登录后可以进入用户资料页面,点击注销按钮即可退出登录。但是,这个系统存在着严重的CSRF攻击漏洞,在攻击者的攻击下,用户的账号信息可能会被篡改。

攻击者可以通过伪造表单数据,利用POST请求来篡改用户的账号信息。具体代码如下:

import requests

url = 'http://localhost:5000/login'
payload = {
    'username': 'pidancode.com', 
    'password': '123456',
    '_csrf_token': 'invalid_csrf_token'
}
headers = {
    'Referer': 'http://localhost:5000/'
}

response = requests.post(url, data=payload, headers=headers)

if response.status_code == 200:
    print(response.content.decode())
else:
    print('Failed: ', response.status_code)

在上述代码中,我们通过POST请求将伪造的用户名、密码和错误的CSRF令牌发送给目标网站,其中Referer字段为攻击者设置的伪装URL。攻击者想要成功地利用CSRF攻击实现用户账号的篡改,必须绕过目标网站的CSRF令牌验证机制。在本例中,我们设置了一条错误的CSRF令牌,这就足以使攻击失败。而如果我们使用正确的CSRF令牌,攻击就会成功。

当然,这只是一种简单的攻击方式,实际上攻击者可以通过多种方式进行CSRF攻击和绕过。因此,前端和后端开发人员需要共同预防并在系统设计防范时耐心思考。

相关文章