【问题标题】:CSRF protection on AJAX authentication in FlaskFlask 中 AJAX 身份验证的 CSRF 保护
【发布时间】: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) 输出的截断版本。显然有些东西不同步,但我不明白它是什么。

【问题讨论】:

    标签: python ajax flask csrf


    【解决方案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 中删除。"
    猜你喜欢
    • 2016-04-26
    • 2017-09-23
    • 1970-01-01
    • 1970-01-01
    • 2014-11-20
    • 2020-05-10
    • 2018-07-08
    • 2015-02-11
    • 2021-05-31
    相关资源
    最近更新 更多