如何使用Python实现跨站请求伪造的攻击与防御

2023-04-17 00:00:00 如何使用 伪造 防御

跨站请求伪造(CSRF)是一种网络攻击方式,攻击者利用受害者在已登录的情况下,通过伪造的请求来执行恶意的操作,例如转账、更改密码等。本篇文章将介绍如何使用Python实现CSRF攻击和防御。

一、CSRF攻击

1.攻击原理

由于受害者已经登录了某个网站,那么该网站的Cookie值已经被存储在受害者浏览器中。攻击者可以伪造一个请求,将该请求发送给受害者,当受害者打开该请求时,该请求将在受害者的浏览器中被执行。此时,由于该请求携带的是伪造的Cookie,因此该请求被认为是合法的,网站无法区分该请求是受害者自己发起的,还是攻击者伪造的。

2.攻击过程

攻击过程如下:

(1)攻击者构造一个包含攻击代码的页面,并将该页面发送给受害者。

(2)当受害者接收到该页面时,会自动执行页面里的攻击代码。

(3)攻击代码会构造一个请求,该请求中包含了攻击者所期望的操作。

(4)请求被发送至目标服务器,由于该请求包含了受害者的Cookie值,因此服务器会误认为该请求是合法的,并执行攻击者所期望的操作。

3.攻击代码

攻击代码如下:

import requests

def csrf_attack():
    # 将攻击代码放在一个页面中,并将该页面发送给受害者。
    url = 'http://www.pidancode.com/attack.html'
    header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
    response = requests.get(url, headers=header)
    print(response.text)

攻击代码的作用是构造一个包含攻击请求的页面,并将该页面发送给受害者。在实际攻击中,攻击者可能会通过社交工程等手段来让受害者打开该页面。

4.攻击演示

为了演示攻击过程,我们可以构造一个被攻击的网站(这里以模拟登录的网站为例)。

攻击网站代码:

from flask import Flask, request, render_template, session, redirect, url_for

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

@app.route('/', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']

        # 简单模拟登录功能,当用户名为'pidancode',密码为'123456'时登录成功。
        if username == 'pidancode' and password == '123456':
            session['logged_in'] = True
            return redirect(url_for('home'))

    return render_template('login.html')

@app.route('/home')
def home():
    if 'logged_in' in session:
        return 'Welcome to Pidancode.com!'
    else:
        return redirect(url_for('login'))

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

登录页面:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Login</title>
</head>
<body>
    <form method="POST" action="">
        <label>Username:</label>
        <input type="text" name="username">
        <br>
        <label>Password:</label>
        <input type="password" name="password">
        <br>
        <input type="submit" value="Login">
    </form>
</body>
</html>

当用户名为'pidancode',密码为'123456'时,登录成功,并显示'Welcome to Pidancode.com!'。

运行攻击网站,然后在另一个窗口中运行攻击代码,具体过程如下:

(1)使用浏览器访问攻击网站,输入正确的用户名和密码,成功登录。

(2)使用同一浏览器访问攻击页面(attack.html),攻击代码自动执行。

(3)在攻击网站中会发现自动执行了一个注销登录的操作。

(4)此时,在攻击页面中再次访问攻击网站(http://localhost:5000/home),发现已经退出登录。说明攻击成功。

二、CSRF防御

1.防御原理

防御CSRF攻击的方法很多,其中最常见的一种是使用验证码。当受害者发送一个请求时,系统会要求受害者先输入验证码,如果验证通过,则系统才会处理该请求,否则该请求将被拒绝。

2.防御代码

防御CSRF攻击的代码如下所示:

from flask import Flask, request, render_template, session, redirect, url_for
import string, random

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

@app.route('/', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']

        # 简单模拟登录功能,当用户名为'pidancode',密码为'123456'时登录成功。
        if username == 'pidancode' and password == '123456':
            session['logged_in'] = True
            session['csrf_token'] = ''.join(random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for x in range(32))
            return redirect(url_for('home'))

    return render_template('login.html')

@app.route('/home', methods=['GET', 'POST'])
def home():
    if 'logged_in' in session:
        if request.method == 'POST':
            # 验证CSRF Token,如果验证不通过,则拒绝该请求。
            if request.form.get('csrf_token') != session.get('csrf_token'):
                return 'Error: Invalid CSRF Token'

            # 处理正确的请求。
            operation = request.form.get('operation')
            if operation == 'logout':
                session.pop('logged_in', None)
                session.pop('csrf_token', None)
                return redirect(url_for('login'))
            else:
                return 'Error: Invalid Operation'

        # 防御CSRF攻击的核心代码,插入一个CSRF Token。
        session['csrf_token'] = ''.join(random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for x in range(32))
        return render_template('home.html', csrf_token=session['csrf_token'])
    else:
        return redirect(url_for('login'))

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

在登录成功后,为了防御CSRF攻击,我们会创建一个CSRF Token,并将该Token存储在用户的会话中,以便在受到请求时进行验证。

当用户发送一个带有操作数据的POST请求时,我们会从前端获取到该请求中的CSRF Token,并根据前端提供的Token来验证该请求。如果验证通过,则继续进行数据处理。

另外,在每次用户访问主页时,都需要重新生成一个CSRF Token,并将该Token插入到网页中,以保证每个请求都是唯一的。

3.防御演示

在使用防御代码后,可以复现上述攻击流程:

(1)使用浏览器访问攻击网站,输入正确的用户名和密码,成功登录。

(2)使用同一浏览器访问攻击页面(attack.html),攻击代码自动执行。

(3)攻击者期望的注销登录操作没有被执行,并且在攻击页面中访问http://localhost:5000/home,收到了'Error: Invalid CSRF Token'消息,说明请求被拒绝。

这表明,我们的CSRF防御代码已经成功地防御了该攻击。

相关文章