Flask 中 AJAX 身份验证的 CSRF 保护

Posted

技术标签:

【中文标题】Flask 中 AJAX 身份验证的 CSRF 保护【英文标题】:CSRF protection on AJAX authentication in Flask 【发布时间】:2014-12-19 13:50:34 【问题描述】:

我想对网站上的登录和注册表单进行 AJAXify。到目前为止,我一直在使用 WTForms 主要是为了它内置的 CSRF 保护,但对于这个项目,我觉得它不值得——额外的抽象层,因此很沮丧,因为它应该很漂亮很简单。

所以我在 Flask 的安全部分遇到了this snippet:

@app.before_request
def csrf_protect():
    if request.method == "POST":
        token = session.pop('_csrf_token', None)
        if not token or token != request.form.get('_csrf_token'):
        abort(403)

def generate_csrf_token():
    if '_csrf_token' not in session:
        session['_csrf_token'] = some_random_string()
    return session['_csrf_token']

app.jinja_env.globals['csrf_token'] = generate_csrf_token

我了解这段代码背后的思考过程。事实上,这一切对我来说都很有意义(我认为)。我看不出有什么问题。

但它不起作用。我对代码所做的唯一更改是将伪函数some_random_string() 替换为对os.urandom(24) 的调用。到目前为止,每个请求都有 403,因为 tokenrequest.form.get('_csrf_token') 永远不会相同。当我打印它们时,这变得很明显——通常它们是不同的字符串,但偶尔,并且似乎没有根本原因,一个或另一个将是Noneos.urandom(24) 输出的截断版本。显然有些东西不同步,但我不明白它是什么。

【问题讨论】:

【参考方案1】:

我认为你的问题是 os.urandom 函数。此函数的结果可能包含无法在 html 中正确解析的符号。因此,当您在 html 中插入 csrf_token 并且不进行任何转义时,您就会遇到所描述的问题。

如何解决。 尝试在 html (see docs) 中转义 csrf_token 或使用其他方法生成 csrf 令牌。例如使用 uuid:

import uuid
...

def generate_random_string():
    return str(uuid.uuid4())
...

【讨论】:

我想我应该提到我对 urandom() 的输出进行了 base64 编码。显然这不是正确的解决方案。有机会我会尽快检查您的解决方案。 我为你的努力投了赞成票,但不幸的是问题仍然存在。在模板中对generate_csrf_token() 的调用工作正常(reqest.form.get('_csrf_token') 正在正确访问令牌),但由于某种原因它不在会话中。它打印为None,我完全不明白,因为app.jinja_env.globals['csrf_token'] 使用它。在调用generate_csrf_token()csrf_protect() 之间的某个地方,它会从会话中消失并根据session.pop('_csrf_token', None) 返回None。但它只是被使用了! 可以给会话配置吗? 我居然最终解决了这个问题,答案很尴尬。当我第一次开始这个项目时,我安装了 WTForms 和 flask-wtf(很大程度上是出于习惯),然后才决定放弃它们。在删除了我的大部分代码后,我意识到是他们内置的 CSRF 保护干扰了我正在尝试做的事情。不过,我非常感谢您花时间帮助我。【参考方案2】:

您可以获得flask-wtf 的便利,而无需繁重,也无需自己动手:

from flask_wtf.csrf import CsrfProtect

然后在初始化时:

CsrfProtect(app)

或:

csrf = CsrfProtect()

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

令牌将在任何时候在应用范围内可用,包括通过jinja2

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

(通过docs)

【讨论】:

请注意:“FlaskWTFDeprecationWarning: "flask_wtf.CsrfProtect" 已重命名为 "CSRFProtect" 并将在 1.0 中删除。"

以上是关于Flask 中 AJAX 身份验证的 CSRF 保护的主要内容,如果未能解决你的问题,请参考以下文章

用于ajax调用的spring security csrf保护

什么是CSRF?

设计用户登录为 CSRF 令牌真实性令牌提供身份验证错误

如何通过 AJAX 使用 Flask-WTForms CSRF 保护?

身份验证控制器中的 Laravel 5.2 Web 中间件导致 csrf 令牌不匹配

jQuery + AJAX + Django = CSRF ? [复制]