CSRF攻击
CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造。
CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。
包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…
造成的问题:个人隐私泄露以及财产安全。
攻击示意图:
如何防御CSRF攻击
csrf_token服务器内部校验过程:
表单提交
1.在表单中携带,隐藏的csrf_token,加密的 (自己设置)
2.在cookie中,设置一个sessionID (服务器设置)
在提交的时候,服务器校验过程:
(1)取出表单中的csrf_token, 使用SECRET_KEY,进行解密, 得到未加密的csrf_token
(2)通过sessionID取出,服务器内部的session空间中的,未加密的csrf_token,比较二者的值是否相等,如果相等则校验通过
非表单提交, ajax提交
1.在headers中设置csrf_token,加密的 (自己设置), 来自于cookie中的 (我们自己设置的)
2.在cookie中,设置一个sessionID (服务器设置)
在提交的时候,服务器校验过程:
(1)取出headers中的csrf_token, 使用SECRET_KEY,进行解密, 得到未加密的csrf_token
(2)通过sessionID取出,服务器内部的session空间中的,未加密的csrf_token,比较二者的值是否相等,如果相等则校验通过
Flask框架中的CSRF保护机制
csrf_token: 为了解决跨站请求伪造,⽽定义的变量
flask中提供了模块flask_wtf⾥⾯提供了⼀个类CSRFProtect来保护
使⽤流程:
1.导⼊模块: from flask_wtf import CSRFProtect
2.使⽤CSRFProtect(app), 保护应⽤程序
3.在表单中,使⽤csrf_token(), ⾃动⽣成⼀个csrf_token值,并且这个值是加密的,需要设置
SECRET_KEY
4.csrfprotect保护的时候对以下请求⽅式做校验:‘POST’, ‘PUT’, ‘PATCH’, ‘DELETE’
代码实现:
在html模板文件中表单提交里需要加上一个隐藏的字段csrf_token
from flask import Flask,render_template,request
from flask_wtf import CSRFProtect
app = Flask(__name__)
app.config["SECRET_KEY"] = "fjdkfkdjfdk"
CSRFProtect(app)
@app.route('/')
def index():
return render_template("file08index.html")
@app.route('/register',methods=["POST"])
def register():
#1.获取参数
username = request.form.get("username")
password = request.form.get("password")
repassword = request.form.get("repassword")
#2.校验,为空校验
if not all([username,password,repassword]):
return "参数填写不完整"
#3.密码需要一致
if password != repassword:
return "密码不一致"
return "注册成功"
if __name__ == '__main__':
app.run(debug=True)
具体实现方法(手动添加):
@app.route('/', methods=["POST", "GET"])
def index():
if request.method == "POST":
# 取到表单中提交上来的参数
username = request.form.get("username")
password = request.form.get("password")
if not all([username, password]):
print('参数错误')
else:
print(username, password)
if username == 'laowang' and password == '1234':
# 状态保持,设置用户名到cookie中表示登录成功
response = redirect(url_for('transfer'))
response.set_cookie('username', username)
return response
else:
print('密码错误')
return render_template('temp_login.html')
#转账页面
@app.route('/transfer', methods=["POST", "GET"])
def transfer():
# 从cookie中取到用户名
username = request.cookies.get('username', None)
# 如果没有取到,代表没有登录
if not username:
return redirect(url_for('index'))
if request.method == "POST":
to_account = request.form.get("to_account")
money = request.form.get("money")
# 取到表单中的token
form_csrf_token = request.form.get('csrf_token')
# cookie中的token
cookie_csrf_token = request.cookies.get('csrf_token', "")
# 做校验。如果校验成功,再进行转账逻辑
if form_csrf_token != cookie_csrf_token:
return "你是坏蛋,非法请求"
print('假装执行转账操作,将当前登录用户的钱转账到指定账户')
return '转账 %s 元到账号 %s 成功' % (money, to_account)
csrf_token = generate_csrf()
# 渲染转换页面,表单中设置csrf_token
response = make_response(render_template('temp_transfer.html', csrf_token=csrf_token))
# 往cookie中添加csrf_token
response.set_cookie('csrf_token', csrf_token)
return response
# 生成 csrf_token
def generate_csrf():
return bytes.decode(base64.b64encode(os.urandom(48)))
if __name__ == '__main__':
app.run(debug=True, port=9000)