详解CSRF跨站点请求伪造

CSRF攻击:
CSRF跨站点请求伪造(Cross—Site Request Forgery):大概可以理解为攻击者盗用了你的身份,以你的名义在恶意网站发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,甚至于购买商品、转账等。
例如:Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户。
详解CSRF跨站点请求伪造_第1张图片
CSRF攻击攻击原理及过程如下:
1.用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3.用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4.网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5.浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
CSRF漏洞检测:
1.检测CSRF漏洞最简单的方法就是抓取一个正常请求的数据包,去掉Referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞。
2.随着对CSRF漏洞研究的不断深入,不断涌现出一些专门针对CSRF漏洞进行检测的工具,如CSRFTester,CSRF Request Builder等,CSRF漏洞检测工具的测试原理如下:使用CSRFTester进行测试时,首先需要抓取我们在浏览器中访问过的所有链接以及所有的表单等信息,然后通过在CSRFTester中修改相应的表单等信息,重新提交,这相当于一次伪造客户端请求。如果修改后的测试请求成功被网站服务器接受,则说明存在CSRF漏洞,当然这些工具也可以被用来进行CSRF攻击。
防御CSRF攻击:
1. 验证 HTTP Referer 字段
根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer(浏览器会自动加上这个字段),它记录了该 HTTP 请求的来源地址。通常情况下,访问一个安全受限页面的请求来自于同一个网站,例如从login页面点击“点我”跳转,就会带着Referer:http://127.0.0.1:5000/login:
详解CSRF跨站点请求伪造_第2张图片
跳转后:
详解CSRF跨站点请求伪造_第3张图片
在以上CSRF攻击的第四步,该请求的 Referer 是指向黑客自己的网站B而不是网站A。因此,要防御 CSRF 攻击,网站A只需要对于每一个请求验证其 Referer 值,如果是A网站的域名,则说明该请求是来自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是黑客的 CSRF 攻击,拒绝该请求。

Referer的优势与劣势:
1.简单易行,开发人员不需要操心 CSRF 的漏洞,只需要在最后给所有安全敏感的请求统一增加一个拦截器来检查 Referer 的值就可以,非常便捷。

#每个请求执行之前先执行before验证请求头中的referer
@app.before_request
def csrf_check_referer():
    # 获取请求头中的referer,login页面不需要检查因为没有,只需要检查后续的跳转页面请求是否带referer字段
    if request.path != "/login":
        referer=request.referrer
        print(referer)
        if referer[:22] != "http://127.0.0.1:5000/":
            return "page not found",404

如果域名不是"http://127.0.0.1:5000/",跳转后的页面404
详解CSRF跨站点请求伪造_第4张图片

2.Referer 的值是由浏览器提供的,并不能保证浏览器自身没有安全漏洞。对于某些浏览器,目前已经有一些方法可以篡改 Referer 值,黑客完全可以修改 Referer 值,这样就可以通过验证,从而进行 CSRF 攻击。
3.即便黑客无法篡改 Referer 值,因为 Referer 值会记录下用户的访问来源,有些用户认为这样会侵犯到他们自己的隐私权,因此用户自己可以设置浏览器使其在发送请求时不再提供 Referer。当他们正常访问网站时,网站会因为请求没有 Referer 值而拒绝合法用户的访问。
2. 在请求中添加 token 并验证
demo代码如下:
app.py需要设置秘钥,使用form表单验证模块flask-wtf对前端数据进行验证,包括前端传回的csrf-token的验证;

import os
from flask import Flask, request, render_template

#初始化application
from wtforms import ValidationError
from registerform import MyForm

app = Flask(__name__)
#设置秘钥
app.config["SECRET_KEY"] = os.urandom(10)

#每个请求执行之前先执行before,验证请求头中的referer字段
@app.before_request
def csrf_check_referer():
    # 获取请求头中的referer,login页面不需要检查因为没有,只需要检查后续的跳转页面请求是否带referer字段
    if request.path != "/login":
        referer=request.referrer
        print(referer)
        if referer[:22] != "http://127.0.1.1:5000/":
            return "page not found",404
            
@app.route("/")
def index():
    return {"user":"lei"}
    
@app.route('/login',methods=['GET','POST'])
def login():
#使用flask-wtf表单验证,会自动加上CSRF攻击的验证
    form = MyForm()
    if request.method == "GET":
        return render_template('index.html', form=form)
    if form.validate_on_submit():
        return 'OK'
    else:
        raise ValidationError(message=form.errors)

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

form表单验证模块:

from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired,Length

class MyForm(FlaskForm):
验证前端传入的name不能为空,长度2-10;
    name = StringField('name', validators=[DataRequired("姓名不能为空"),Length(2,10,"名字长度错误")])

html文件:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>flasktitle>
head>
<body>
<div style="color:red">欢迎~~~div>
<form method="POST" action="/login">
#请求页面时会加上csrf_token,提交表单时会带上这个隐藏的token以和参数一起提交给服务器;
{{ form.csrf_token }}
<lable>姓名:lable>
    <input type="text" name="name" value="">
<input type="submit" value="submit">
form>
<a href="http://127.0.0.1:5000">点我a>
body>
html>

1.在客户端向后端请求界面数据的时候,需要在 Form 表单中添加一个隐藏的的字段,值是 csrf_token
详解CSRF跨站点请求伪造_第5张图片
2.在用户点击提交的时候,会带上这个值向后台发起请求;
详解CSRF跨站点请求伪造_第6张图片
3.后端接受到请求,会比较值是否正确,如果没取到或者不正确,代表不是正常的请求,不执行下一步操作。
如下用postman直接发起请求,则会报csrf_token错误;
详解CSRF跨站点请求伪造_第7张图片

你可能感兴趣的:(flask,python,开发语言,后端)