Flask CSRFToken保护、CORS跨域请求

Flask CSRFToken保护、CORS跨域请求

  • csrf具体过程
  • csrf保护实现
  • 前后端不分离
    • 自定义错误响应和关闭保护
    • ajax提交
    • 故障排除
  • 前后端分离项目
  • CORS跨越请求

官方文档: http://www.pythondoc.com/flask-wtf/csrf.html#id4

csrf具体过程

根据 csrf_token 校验原理,具体操作步骤有以下几步:

  • 1.后端生成 csrf_token 的值,在前端请求登录或者注册界面的时候将值传给前端,传给前端的方式可能有以下两种:
    • 1 在模板中的From表单中添加隐藏字段
    • 2.使用 cookie 的方式将 csrf_token 传给前端
  • 2.在前端发起请求时,在表单或者在请求头中带上指定的 csrf_token
  • 3.后端在接受到请求之后,取到前端发送过来的 csrf_token,与第1步生成的 csrf_token 的值进行校验
  • 4.校验通过代表是正常的请求,否则可能就是伪造,不让通过

csrf保护实现

为了能够让所有的视图函数受到 CSRF 保护,你需要开启 CsrfProtect 模块:

from flask_wtf.csrf import CsrfProtect
CsrfProtect(app)

像任何其它的 Flask 扩展一样,你可以惰性加载它:

from flask_wtf.csrf import CsrfProtect
csrf = CsrfProtect()

def create_app():
    app = Flask(__name__)
    csrf.init_app(app)

前后端不分离

前端表单提交代码:

如果模板中存在表单,

<form method="post" action="/">
    {{ form.csrf_token }}
form>

但是如果模板中没有表单,你仍然需要一个 CSRF 令牌:

<form method="post" action="/">
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
form>

自定义错误响应和关闭保护

无论何时未通过 CSRF 验证,都会返回 400 响应。你可以自定义这个错误响应:

@csrf.error_handler
def csrf_error(reason):
    return render_template('csrf_error.html', reason=reason), 400

你也可以在所有的视图中禁用 CSRF 保护,通过设置 WTF_CSRF_CHECK_DEFAULT 为 False,仅当你需要的时候选择调用 csrf.protect()。

@app.before_request
def check_csrf():
    if not is_oauth(request):
        csrf.protect()

当设置对所有视图启动CSRF保护时,也可以让某些视图函数关闭csrf保护

@csrf.exempt
@app.route('/foo', methods=('GET', 'POST'))
def my_handler():
    # ...
    return 'ok'

ajax提交

不需要表单,通过 AJAX 发送 POST 请求成为可能。Flask0.9.0 版本后这个功能变成可用的。

  • 假设你已经使用了 CsrfProtect(app),你可以通过 {{ csrf_token() }} 获取 CSRF
    令牌。这个方法在每个模板中都可以使用,你并不需要担心在没有表单时如何渲染 CSRF 令牌字段。

我们推荐的方式是在 meta 标签中渲染 CSRF 令牌:

<meta name="csrf-token" content="{{ csrf_token() }}">

在 script 标签中渲染同样可行:

<script type="text/javascript">
    var csrftoken = "{{ csrf_token() }}"
script>

下面的例子采用了在 meta标签渲染的方式, 在 script 中渲染会更简单,你无须担心没有相应的例子。

无论何时你发送 AJAX POST 请求,为其添加 X-CSRFToken 头:

var csrftoken = $('meta[name=csrf-token]').attr('content')

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken)
        }
    }
})

故障排除

当你定义你的表单的时候,如果犯了这个错误 : 从 wtforms 中导入 Form 而不是从 flask.ext.wtf 中导入,CSRF 保护的大部分功能都能工作(除了 form.validate_on_submit()),但是 CSRF 保护将会发生异常。在提交表单的时候,你将会得到 Bad Request/CSRF token missing or incorrect 错误。这个错误的出现就是因为你的导入错误,而不是你的配置问题。

前后端分离项目

我们需要生成token值并利用请求钩子设置cookie,然后前端就能获取到cookie里面的csrf_token:

from flask_wtf.csrf import generate_csrf

@app.after_request
def after_request(response):
    # 调用函数生成csrf token
    csrf_token = generate_csrf()
    # 设置cookie传给前端
    response.set_cookie('csrf_token', csrf_token)
    return response
  1. 在前端请求时带上 csrf_token 值
  2. 根据登录和注册的业务逻辑,当前采用的是 ajax 请求
  3. 所以在提交登录或者注册请求时,需要在请求头中添加 X-CSRFToken 的键值对
$.ajax({
	...
	headers: {"X-CSRFToken": getCookie("csrf_token")},
	...
})

function getCookie(name) {
    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    return r ? r[1] : undefined;
}

CORS跨越请求

下载包:

pip install flask-cors

官方文档:http://flask-cors.readthedocs.io/en/latest/

例子:

from flask_cors import CORS
app = Flask(__name__)
# r'/*' 是通配符,让本服务器所有的URL 都允许跨域请求
CORS(app, supports_credentials=True, resources=r'/*')

你可能感兴趣的:(Flask)