• 19、Flask实战第19天:CSRF攻击与防御


    CSRF攻击原理

    网站是通过cookie来实现登录功能的。而cookie只要存在浏览器中,那么浏览器在访问这个cookie的服务器的时候,就会自动的携带cookie信息到服务器上去。那么这时候就存在一个漏洞了。如果你访问了一个恶意网站,这个网站可以在网页源码中插入JS代码,使用JS代码给其他服务器发送请求。因为在发送请求的时候,浏览器会自动把cookie发送给对应的服务器,这时候相应的服务器就不知道这个请求是伪造的。从而达到在用户不知情的情况下,给某个服务器发送了一个请求。

     防御CSRF攻击原理

    CSRF攻击的要点就是在向服务器发送请求的时候,相应的cookie会自动的发送给对应的服务器。造成服务器不知道这个请求是用户发起的还是伪造的。这时候,我们可以在用户每次访问有表单页面的时候,在网页源码中加一个随机的字符串叫做csrf_token在cookie中也加入一个相同值的csrf_token字符串。以后给服务器发送请求的时候,必须在body中以及cookie中都携带 csrf_token,服务器只有检测到cookie中的csrf_token和body中的csrf_token都相同,才认为这个请求是正常的。否则就是伪造的,那么黑客就没办法伪造请求了。

    Flask防御CSRF攻击

    使用flask_wtf.CSRFProtect来包裹app

    import os
    from flask_wtf import CSRFProtect
    app.config['SECRET_KEY'] = os.urandom(24) #和session一样,必须要配置一个secret key参与加密
    CSRFProtect(app)

    然后在模板form表单中添加一个input标签,加载csrf_token

    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>

     以上配置就能够在,用户在提交表单后一并把表单中的csrf_token提交到服务器,并和服务器上的csrf_token做对比,这样就能够识别请求是否是伪造的,从而避免CSRF攻击

    AJAX处理CSRF漏洞

    如果我们的表单是通过AJAX提交处理的。示例如下

    <head>
        ...
        <!--我们这里的AJAX代码是用JQuery写的,因此要引入Jquery-->
        <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
        <!--AJAX代码在login.js文件中-->
        <script src="{{ url_for('static', filename='login.js') }}"></script>
    </head>
    
    
    <body>
        <form action="" method="post">
            <table>
                <tr>
                    <td>邮箱:</td>
                    <td><input name="email" type="text" /></td>
                </tr>
    
                <tr>
                    <td>密码:</td>
                    <td><input name="password" type="text" /></td>
                </tr>
    
                <tr>
                    <td></td>
                    <td><input type="submit" value="提交"  id="submit"/></td>
                </tr>
            </table>
        </form>
    </body>
    login.html

     login.js

    ...
    
    class LoginView(views.MethodView):
        def get(self):
            return render_template('login.html')
    
        def post(self):
            email = request.form.get('email')
            password = request.form.get('password')
            if email == 'heboan@qq.com' and password == '123456':
                return '登录成功'
            else:
                return '登录失败'
    
    
    app.add_url_rule('/login/', view_func=LoginView.as_view('login'))
    login视图

    以上我们已经成功使用了AJAX处理表单。现在我们启用CSRF,主程序中代码如下:

    import os
    from flask_wtf import CSRFProtect
    app.config['SECRET_KEY'] = os.urandom(24) #和session一样,必须要配置一个secret key参与加密
    CSRFProtect(app)

    然后我们再次用正确的账号密码登录,发现出现错误,因为我们启用防csrf攻击,但是AJAX那里并没有携带csrf_token,所以请求失败

    因此,我们需要在login.html中添加一个表单用来存放token

    <form action="" method="post">
        <!--添加一个隐藏input用来放置csrf_token-->
        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
        <table>
            <tr>
                <td>邮箱:</td>
                <td><input name="email" type="text" /></td>
            </tr>
    
            <tr>
                <td>密码:</td>
                <td><input name="password" type="text" /></td>
            </tr>
    
            <tr>
                <td></td>
                <td><input type="submit" value="提交"  id="submit"/></td>
            </tr>
        </table>
    </form>

    编辑AJAX代码段,把表单中的csrf_token传给服务器作比对

    再次登录(强制刷新浏览器,因为js有缓存)

    AJAX处理CSRF漏洞--优化代码 

    csrf_token可以放置的head中。在实际项目中,我们一般也是放在头部,这样做有个好处就是:当多处需要用到csrf_token,我们就不需要多次写一个隐藏的csrf_token表单。特别是在模板继承的时候,我们只要在父模板中的head中加入csrf配置,其子模板就可以直接继承了。

    编辑login.html

    <head>
        <!--在head中加上此配置-->
        <meta name="csrf-token" content="{{ csrf_token() }}">
        ...
    </head>
    
    
    <!--表单中就不需要添加一个csrf_token表单了-->
    <form action="" method="post">
        <table>
            <tr>
                <td>邮箱:</td>
                <td><input name="email" type="text" /></td>
            </tr>
    
            <tr>
                <td>密码:</td>
                <td><input name="password" type="text" /></td>
            </tr>
    
            <tr>
                <td></td>
                <td><input type="submit" value="提交"  id="submit"/></td>
            </tr>
        </table>
    </form>

     新建一个heboan_ajax.js, 来封装

    修改login.js

    最后在login.html中要引入封装的js

    <head>
        ...
        <meta name="csrf_token" content="{{ csrf_token() }}">
        <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
        <script src="{{ url_for('static', filename='heboan_ajax.js') }}"></script>
        <script src="{{ url_for('static', filename='login.js') }}"></script>
    
    </head>

    这样实现的好处就是以后在其他地方处理CSRF的时候,我们直接把我们封装好的heboan_ajax拿来用就可以了,模板那边可以通过继承实现避免重复定义csrf_token表单和js引入

  • 相关阅读:
    swift开发之--可选类型说明,类型判断 is 与类型转换 as
    壹佰文章最全总结| 《关于ASP.NETCore的分享之路》
    Blog.Core 项目已完成升级.NET5.0
    BCVP,想真正为社区做努力的开发者们
    centos7 安装 certbot 动态更新Let's encrypt 证书实现nginx免费ssl证书
    CentOS 7.5 安装 CDH6.3.2
    cloud-init在centos中关于静态ip和hostname的配置
    wait为什么要在同步块中使用? 为什么sleep就不用再同步块中?
    Java字节码技术 static、final、volatile、synchronized关键字的字节码体现 转
    说说互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景 转
  • 原文地址:https://www.cnblogs.com/sellsa/p/9369019.html
Copyright © 2020-2023  润新知