当csrf_enabled为True(设置SECRET_KEY)时,Flask-WTF引发错误

我遇到了有关flask-WTF的csrf保护的问题。

像这样实例化表单时:

uform = UserForm(csrf_enabled=False)

一切正常,并且表格正确显示。但是:

uform = UserForm()

导致TypeError:

Traceback (most recent call last):
  File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/flask/app.py",line 2463,in __call__
    return self.wsgi_app(environ,start_response)
  File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/flask/app.py",line 2449,in wsgi_app
    response = self.handle_exception(e)
  File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/flask/app.py",line 1866,in handle_exception
    reraise(exc_type,exc_value,tb)
  File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/flask/_compat.py",line 39,in reraise
    raise value
  File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/flask/app.py",line 2446,in wsgi_app
    response = self.full_dispatch_request()
  File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/flask/app.py",line 1952,in full_dispatch_request
    return self.finalize_request(rv)
  File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/flask/app.py",line 1969,in finalize_request
    response = self.process_response(response)
  File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/flask/app.py",line 2268,in process_response
    self.session_interface.save_session(self,ctx.session,response)
  File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/flask/sessions.py",line 387,in save_session
    samesite=samesite,File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/werkzeug/wrappers/base_response.py",line 481,in set_cookie
    samesite=samesite,File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/werkzeug/http.py",line 1163,in dump_cookie
    buf = [key + b"=" + _cookie_quote(value)]
TypeError: unsupported operand type(s) for +: 'NoneType' and 'bytes'

设置了SECRET_KEY(WTF_CSRF_SECRET_KEY也是如此,只是为了确保flask-WTF不会期望它,即使根据文档是可选的)。

以下是(相关)代码的其余部分:

admin / routes.py

from flask import current_app as app
from .. import db
from ..models import User
from .forms import UserForm

# Set up a Blueprint
admin_bp = Blueprint('admin_bp',__name__,template_folder='templates',static_folder='static')

@admin_bp.route("users/add/",methods=["GET","POST"])
def add_user():
    #Add user to the DB
    add_user = True

    #uform = UserForm(csrf_enabled=True)
    uform = UserForm()

    if uform.validate_on_submit():
        user = User(username=uform.username.data,email=uform.email.data,admin=uform.admin.data,address=uform.address.data,delivery=uform.delivery.data)
        user.pwhash(uform.password.data)

        try:
            db.session.add(user)
            db.session.commit()
            flash("Benutzer '",user.username,"' erfolgreich hinzugef  gt.")
        except:
            flash("Fehler: Benutzer konnte nicht hinzugef  gt werden. Existiert Benutzer bereits?")

        return redirect(url_for("admin_bp.users"))

    return render_template("/user.html",action="Hinzuf  gen",add_user=add_user,uform=uform,title="Benutzer hinzuf  gen")

admin / forms.py

from flask_wtf import flaskForm
from wtforms import StringField,SubmitField,PasswordField,BooleanField,SelectField
from wtforms.validators import DataRequired,Email

class UserForm(flaskForm):
    username = StringField("username",validators=[DataRequired()])
    email = StringField("Email",validators=[DataRequired(),Email()])
    password = StringField("Neues Passwort")
    address = StringField("Adresse")
    admin = BooleanField("Admin?")
    delivery = SelectField("Delivery",choices=[("1","test1"),("2","test2")])
    submit = SubmitField("OK")

__ init __。py:

from flask_sqlalchemy import SQLAlchemy
from flask_static_compress import flaskStaticCompress
from flask_bootstrap import Bootstrap
from flask_wtf.csrf import CSRFProtect

# Globally accessible libraries
db = SQLAlchemy()
bs = Bootstrap()
csrf = CSRFProtect()

def create_app():
    app = flask(__name__,instance_relative_config=False,static_folder="static",template_folder="templates")
    from config import Config
    app.config.from_object('config.DevConfig')
    db.init_app(app)
    bs.init_app(app)
    csrf.init_app(app)

    with app.app_context():

        from .models import Image,Gallery,ImageGalleryMap,Delivery,ImageDeliveryMap,Blogpost,User,Log
        db.create_all()

        compress = flaskStaticCompress(app)

        # Register Blueprints
        from website.admin.routes import admin_bp
        from website.delivery.routes import delivery_bp
        from website.landing.routes import landing_bp
        from website.public.routes import public_bp

        app.register_blueprint(admin_bp,url_prefix='/admin')
        app.register_blueprint(delivery_bp,url_prefix='/delivery')
        app.register_blueprint(landing_bp,url_prefix='/landing')
        app.register_blueprint(public_bp,url_prefix='/')

        return app

有人知道如何解决此问题吗? 出于(非常明显的)原因,我不太热衷于设置WTF_CSRF_ENABLED = False ...

jakela 回答:当csrf_enabled为True(设置SECRET_KEY)时,Flask-WTF引发错误

我可以复制示例中给出的回溯的唯一方法是app.secret_key是否有效,但是app.session_cookie_name设置为None

请参阅以下我用于测试的脚本。运行此脚本并访问http://127.0.0.1:5000会引发TypeError: unsupported operand type(s) for +: 'NoneType' and 'bytes',这是您上面发布的确切例外。如果未设置秘密密钥,则在尝试实例化表单时,在flask尝试为请求服务之前,它会引发KeyError: 'A secret key is required to use CSRF.',从而导致另一个错误。这是脚本:

from flask import Flask,render_template_string
from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField


app = Flask(__name__)
# comment out below and a KeyError is raised,not a TypeError.
# So the fact that the code raises the TypeError from trying to generate
# a cookie means that the secret key is set,the form is instantiating and
# flask is trying to serve the request. So we can rule out this being a
# problem with secret key not set.
app.secret_key = "lskdjflksdj"
# comment out below and the app runs
app.session_cookie_name = None


class MyForm(FlaskForm):
    a_str = StringField()
    submit = SubmitField()


template = """
<form action="" method="POST">
{{form.a_str()}}
{{form.submit()}}
</form>
"""


@app.route("/",methods=["GET","POST"])
def route():
    # setting csrf_enabled=False makes the problem go away.
    form = MyForm(csrf_enabled=True)
    return render_template_string(template,form=form)


if __name__ == "__main__":
    app.run(debug=True)

那为什么在表单上设置csrf_enabled=False会阻止错误?错误发生时flask生成的令牌是csrf令牌。当我们在表单上禁用csrf检查时,不再需要生成该令牌,并且不会遇到错误。

在表单上切换csrf保护会直接影响错误是否产生,这一事实确实使它看起来像是密钥配置问题,但真正的问题是{{1的值为什么是}}参数传递到key dump_cookie()

如果您遵循Traceback,您将看到flask库内部的最后一个调用在这里:NoneType。这是the source

File "/home/charlotte/hochzeit/venv/lib/python3.6/site-packages/flask/sessions.py",line 387,in save_session

您可以see for yourself将传递给上述 response.set_cookie( name,val,expires=expires,httponly=httponly,domain=domain,path=path,secure=secure,samesite=samesite,) 的第一个参数传递给set_cookie中的key参数,该参数本身传递给{{1 }} werkzeug.wrappers.base_response.set_cookie中的参数(错误消息中的key)。

上面werkzeug.http.dump_cookie片段中None的值在name函数中为defined earlier,而body of the get_cookie_name method仅为save_session

name = self.get_cookie_name(app)的默认值为"session",但是在您的配置中,它会被return app.session_cookie_name覆盖。

本文链接:https://www.f2er.com/3144333.html

大家都在问