【发布时间】:2021-05-05 04:45:48
【问题描述】:
我在我的 Flask webapp 中收到来自常规 PUT 请求的 CSRF 错误。
我不明白为什么会出现这些错误,因为 PUT 请求不涉及任何形式。
我关注了文档here 并像这样使用 CSRF 保护初始化我的应用程序:
cat app/__init__.py
...
from flask_wtf.csrf import CSRFProtect, generate_csrf, validate_csrf
...
csrf = CSRFProtect()
...
def create_app(config_class=Config):
app = Flask(__name__)
...
csrf.init_app(app)
...
来自 javascript 的调用是
cat js/js_script1.js
...
queryUrl = 'https://localhost/put_request1';
let fetchData = {
method: 'PUT'
};
let response = await fetch(queryUrl, fetchData);
我在浏览器和后端看到错误 - Error: 400 Bad Request: The CSRF token is missing.
文档主要讨论表单上下文中的 CSRF 和 ajax,但在这种情况下,我使用的是 fetch(在这件事上 fetch 与 ajax 相同吗?)并且没有任何形式。
在this link部分中:CSRF令牌是如何构造的有一个代码sn-p,它使用flask_wtf函数generate_csrf创建csrf_token。
我试图在 app/__init__.py 中实现它,这导致了其他不相关的错误 (RuntimeError: Working outside of request context)。
在here 中,csrf_token 是从 html 页面中的“csrf-token”元素中读取的。
const csrfToken = document.querySelector("[name='csrf-token']").content
但我没有 csrf-token
为了比较,我打开了我的其他页面之一,它确实有一个表单,在那里我确实看到了 csrf_token 元素:
<input id="csrf_token" name="csrf_token" type="hidden" value="ImI...">
在 chrome 调试器 -> 应用程序 -> cookie -> https://localhost cookie 我只看到 session 令牌,但看不到 csrf_token
问题:
- 初始化我的 Flask 应用程序时是否需要生成 csrf_token?
(据我了解,Flask 会自动为 FlaskForm 执行此操作,但在这种情况下,我没有涉及 FlaskForm) - 如果确实需要生成 csrf_token,如何为常规 POST/PUT 提取请求生成 csrf_token?
编辑 1:
根据@Detlef 的回复,我又做了一次尝试。
我尝试遵循here 中的 Javascript 请求指南以使用以下命令获取 csrf_token,但未成功:
var csrf_token = "{{ csrf_token() }}";
我了解如果此代码嵌入到 html 页面中,那么 jinja2
将从函数csrf_token()返回的值代入变量csrf_token
在我的例子中,我从 javascript 文件(包含在 html 页面中)发出 fetch 请求。
所以我不明白在这种情况下如何处理命令。
我对 javascript 代码进行了以下更改:
var csrf_token = "{{ csrf_token() }}";
console.log('csrf_token', csrf_token);
let headersData = {
'Content-Type': 'application/json',
'X-CSRFToken': csrf_token,
'X-CSRF-TOKEN': csrf_token,
'X-CSRF-Token': csrf_token,
};
let fetchData = {
method: 'PUT',
headers: headersData
};
queryUrl = 'https://localhost/put_request1';
let response = await fetch(queryUrl, fetchData);
控制台中显示的 csrf_token 变量的值是csrf_token: {{ csrf_token() }}
这显然是错误的(没有任何解释)。
这会导致浏览器和后端出现另一个 CSRF 错误:
Error: 400 Bad Request: The CSRF token is invalid.
编辑 2:
根据@Detlef I 的指导:
- 将元标记添加到 .html 页面
<meta content="{{ csrf_token() }}" name="csrf-token">
- 检索了 javascript 中的 csrf_token 使用:
const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");
这解决了问题。
我没有看到 CSRF 错误。
我还在 chrome-devtools -> Networks 中检查了失败的请求,并且确实在请求标头 x-csrf-token 中看到了预期。
x-csrf-token: ImI0ODY...
【问题讨论】:
-
我已经更新了我的答案。我希望这会有所帮助。
-
@Detlef,它确实解决了我的问题。谢谢!
标签: flask csrf csrf-token