正确获取和设置 csrf 令牌

Posted

技术标签:

【中文标题】正确获取和设置 csrf 令牌【英文标题】:Fetching and setting the csrf token correctly 【发布时间】:2021-07-28 17:33:52 【问题描述】:

我正在尝试创建一个使用flask_corsflask_wtf 的简单应用程序。但是,当我使用 flask_wtf 时,当我尝试发送 POST 请求时,会收到一条回复说 CSRF 令牌丢失

这是我的后端应用程序(在端口 3080 上运行):

__init__.py

from flask import Flask
from flask_cors import CORS
from flask_wtf.csrf import CSRFProtect

cors = CORS()
csrf = CSRFProtect()

def create_app(config):

    app = Flask(__name__, instance_relative_config=False)

    app.config['TESTING'] = True 
    app.config['DEBUG'] = True
    app.config['JSON_SORT_KEYS'] = False
    app.config['SECRET_KEY'] = 'You will never guess me!'
    app.config['SESSION_COOKIE_HTTPONLY']= True
    app.config['REMEMBER_COOKIE_HTTPONLY'] = True
    app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'
    
    cors.init_app(app)
    csrf.init_app(app)

    with app.app_context():

        from app.views.sanity_bp import sanity_bp

        app.register_blueprint(sanity_bp, url_prefix='/api/sanity')

        return app

sanity_bp.py

from flask import Blueprint, jsonify, request
from flask_cors import cross_origin
from flask_wtf.csrf import generate_csrf

sanity_bp = Blueprint('sanity_bp', __name__)

@sanity_bp.route('/get/csrf', methods=['GET'])
def get_csrf():
    return jsonify('csrf_token': generate_csrf()), 200

@sanity_bp.route('/post', methods=['POST'])
@cross_origin()
def post():
    return jsonify('sanity: post request'), 200

这里我使用get_csrf()方法获取CSRF令牌。

前端(运行在8080端口)如下:

<template>
  <div>
    Hello stack <b>overflow</b>!
  </div>
</template>

<script>
import axios from 'axios';

const API = axios.create(
  baseURL: 'http://127.0.0.1:3080/api',
);

const csrf = () => 
  API
    .get('/sanity/get/csrf',  credentials: 'same-origin' )
    .then((res) => 
      API.defaults.headers.post['x-csrf-token'] = res.data.csrf_token;
    )
    .catch((err) => 
      console.log('err = ', err);
    );
;

export default 
  name: 'app',
  methods: 
    sanity() 
      API
        .post('/sanity/post',  message: 'ping' )
        .then((res) => 
          console.log('res.data = ', res.data);
        )
        .catch((error) => 
          console.error('error = ', error);
        );
    ,
  ,
  created() 
    csrf();
    this.sanity();
  ,
;
</script>

所以当调用created() 方法时,我从后端获取CSRF 令牌并将其设置为API 函数头中的x-csrf-token 值。

但是,当我在 sanity() 方法中使用 POST 请求时,我收到一个 400 错误,说明未设置 CSRF 令牌。此外,x-csrf-token 键在请求标头中不可见。为什么不呢?请问我在获取令牌时哪里出错了?

【问题讨论】:

【参考方案1】:

您在前端的csrf 函数是异步的,您需要等到它完成后才能在created 函数中创建this.sanity();。你可以使用async/await

...
async created() 
  await csrf();
  await this.sanity();
,
...

【讨论】:

非常感谢您的建议...我现在成功通过了X-CSRF-Token,但收到了The CSRF session token is missing 错误响应。我认为这是因为会话是空的(?)作为flask_wtf/csrf.py 中的validate_csrf 方法,即session = &lt;SecureCookieSession &gt; 不太熟悉flask-wtf,但从documentation我看到他们使用X-CSRFToken标头。你确定X-CSRF-Token 是正确的标题吗? 是的,我将其更改为X-CSRFToken,但得到了同样的错误。我很确定这是会话的东西,所以我会调查一下。

以上是关于正确获取和设置 csrf 令牌的主要内容,如果未能解决你的问题,请参考以下文章

CSRF 令牌丢失或不正确。 Django + AngularJS

javascript 客户端设置 CSRF 令牌是不是不安全?

Django 表单中的 CSRF 令牌丢失或不正确

Django 1.9 AJAX 表单 CSRF 令牌 403 错误 - “未设置 CSRF cookie”

Django navigator.sendbeacon csrf 保护

Spring Security 在登录响应中使用新的会话令牌设置 CSRF