Python中的CSRF攻击与表单重复提交
CSRF攻击(Cross-Site Request Forgery)是一种常见的网络攻击方式,也叫做“跨站请求伪造”。攻击者利用用户已登录的身份,在用户不知情的情况下,向指定网站发送恶意请求,以达到攻击目的。通常,这种攻击会利用浏览器对cookie的自动管理机制,实现对用户账号的控制。
在Python中,可以使用Flask框架的Flask-WTF扩展库来防止CSRF攻击。Flask-WTF提供了CSRF保护机制,可以自动为表单添加验证token。
相比之下,表单重复提交指的是用户再次提交同一份表单,而不是恶意攻击。如果一个表单可以被多次提交并重复执行同一操作,可能会导致数据不一致或重复操作等问题。
为了防止表单重复提交,可以在表单中添加一个唯一的token值,每次提交表单时都验证该token值是否有效,如果无效则拒绝重复提交。可以通过UUID等方式生成唯一的token值。
下面是一个使用Flask-WTF实现CSRF保护的例子:
from flask import Flask, render_template, request from flask_wtf.csrf import CSRFProtect from flask_wtf import FlaskForm from wtforms import StringField, SubmitField app = Flask(__name__) app.config['SECRET_KEY'] = 'secret_key' csrf = CSRFProtect(app) class MyForm(FlaskForm): name = StringField('Name') submit = SubmitField('Submit') @app.route('/', methods=['GET', 'POST']) def index(): form = MyForm() if form.validate_on_submit(): name = form.name.data return f'Hello {name}!' return render_template('index.html', form=form) if __name__ == '__main__': app.run(debug=True)
在上面的例子中,我们首先导入了Flask、Flask-WTF和WTForms等库,然后定义了一个表单类MyForm,其中包含一个文本框和一个提交按钮。在路由函数中,我们创建了一个表单实例,并判断它是否通过验证,如果通过了就输出欢迎信息,并使用render_template函数将表单呈现到模板中。
在模板文件(index.html)中,我们使用Flask-WTF提供的宏(helpers)来自动为表单添加CSRF保护机制。下面是index.html的代码:
<!DOCTYPE html> <html> <head> <title>Flask-WTF CSRF Protection Example</title> </head> <body> <h1>Flask-WTF CSRF Protection Example</h1> <form method="POST" action="/"> {{ form.hidden_tag() }} <!-- 添加CSRF保护 --> {{ form.name.label }} {{ form.name }}<br> {{ form.submit }} </form> </body> </html>
在模板文件中,我们首先引入了Flask-WTF的helpers,然后在form标签中调用了hidden_tag()函数用于自动添加CSRF保护。注意,hidden_tag()函数会自动为表单添加一个隐藏的input标签,并设置一个token值,用于验证表单提交时的CSRF攻击。最后,我们输出了表单中的两个控件,一个是文本框,一个是提交按钮。
如果不使用Flask-WTF,自己手动实现CSRF保护也是很简单的,可以通过以下步骤来实现:
- 生成一个token值,在每次请求时将该token添加到表单或url参数中;
- 每次提交表单或处理请求时,验证该token是否有效,如果无效则返回错误;
下面是一个使用手动实现CSRF保护的例子:
from flask import Flask, render_template, request, session import uuid app = Flask(__name__) app.secret_key = 'secret_key' @app.route('/', methods=['GET', 'POST']) def index(): if request.method == 'GET': token = str(uuid.uuid4()) # 生成一个token值 session['token'] = token return render_template('index.html', token=token) elif request.method == 'POST': token = session.pop('token') # 获取并删除token if token != request.form.get('token'): # 验证token是否有效 return 'Invalid token' name = request.form.get('name') return f'Hello {name}!' if __name__ == '__main__': app.run(debug=True)
在上面的例子中,我们首先创建了一个唯一的token值,并将它保存到session中。在GET请求中,我们将该token值传递并呈现到模板中(index.html),在POST请求中我们取出该token值并验证它是否有效。如果有效,我们取出表单中的name值并输出欢迎信息。
下面是index.html的代码:
<!DOCTYPE html> <html> <head> <title>Manually CSRF Protection Example</title> </head> <body> <h1>Manually CSRF Protection Example</h1> <form method="POST" action="/"> <input type="hidden" name="token" value="{{ token }}"> <!-- 添加token --> <label for="name">Name:</label> <input type="text" name="name" id="name"><br> <input type="submit" value="Submit"> </form> </body> </html>
在模板文件中,我们手动为表单添加了一个隐藏的input标签,并设置了一个token值,用于验证表单提交时的CSRF攻击。注意,我们还需要在表单中添加一个文本框和提交按钮。
相关文章