it-swarm.com.de

Die Formularüberprüfung schlägt fehl, weil CSRF fehlt

Vor einigen Tagen habe ich meine lokale Kolbenumgebung zurückgesetzt, ohne die Abhängigkeiten über einen pip freeze erfasst zu haben, bevor ich sie löschte. Daher musste ich die neueste Version des gesamten Stacks erneut installieren.

Aus heiterem Himmel kann ich jetzt nicht mehr mit Formularen validieren. Flask behauptet, CSRF würde fehlen.

def register():
    form = RegisterForm()
    if form.validate_on_submit():
       ...
    return make_response("register.html", form=form, error=form.errors)

Beim ersten Senden einer Get rufe ich wie erwartet einen leeren form.errors ab .__ Jetzt fülle ich das Formular aus und sende es ab. form.errors zeigt an: {'csrf_token': [u'CSRF token missing']}

Das ist so seltsam. Ich frage mich, ob Flask-WTF sich verändert hat und ich es falsch benutze.

Ich kann deutlich sehen, dass der form.CSRF_token existiert, warum behauptet er, dass er fehlte?

CSRFTokenField: <input id="csrf_token" name="csrf_token" type="hidden" value="1391278044.35##3f90ec8062a9e91707e70c2edb919f7e8236ddb5">

Ich habe die Arbeitsvorlage nie angerührt, aber ich poste sie trotzdem hier:

{% from "_formhelpers.html" import render_field %}
{% extends "base.html" %}
{% block body %}
<div class="center simpleform">
    <h2>Register</h2>
    {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
    <form class="form-signin" action="{{ url_for('register') }}" method=post>
        {{form.hidden_tag()}}
        <dl>
            {{ render_field(form.name) }}
            {{ render_field(form.email) }}
            {{ render_field(form.password) }}
            {{ render_field(form.confirm) }}
            <dd><input type=submit value=Register class='btn btn-primary'>
        </dl>
    </form>
</div>
{% endblock %}

Ist das ein neuer Fehler?

UPDATE:

Ich habe alles neu installiert und das Problem bleibt bestehen. 

Wie Martijn vorschlug, debugge ich in der folgenden Methode in flask_wtf:

def validate_csrf_token(self, field):
        if not self.csrf_enabled:
            return True
        if hasattr(request, 'csrf_valid') and request.csrf_valid:
            # this is validated by CsrfProtect
            return True
        if not validate_csrf(field.data, self.SECRET_KEY, self.TIME_LIMIT):
            raise ValidationError(field.gettext('CSRF token missing'))

Die letzte Bedingung löst den Validierungsfehler aus.

field.data = "1391296243.8##1b02e325eb0cd0c15436d0384f981f06c06147ec"
self.SECRET_KEY = None (? Is this the problem)
self.TIME_LIMIT = 3600

Und Sie hatten recht, der HMAC-Vergleich schlägt fehl. Beide Werte sind in jedem Fall unterschiedlich.

return hmac_compare == hmac_csrf

Ich habe sowohl SECRET_KEY als auch CSRF_SESSION_KEY in meiner Konfiguration definiert.

18
Houman

Die Flask-WTF-CSRF-Infrastruktur lehnt ein Token ab, wenn:

  • das Token fehlt. Nicht der Fall hier, Sie können das Token im Formular sehen.

  • es ist zu alt (Standardablauf ist auf 3600 Sekunden oder eine Stunde eingestellt). Legen Sie das TIME_LIMIT-Attribut in Formularen fest, um dies zu überschreiben. Wahrscheinlich hier nicht der Fall.

  • wenn in der aktuellen Sitzung kein 'csrf_token'-Schlüssel gefunden wird. Sie können den Session-Token anscheinend sehen, also ist auch er aus.

  • Wenn die HMAC-Signatur nicht übereinstimmt; Die Signatur basiert auf dem zufälligen Wert, der in der Sitzung unter dem Schlüssel 'csrf_token' festgelegt ist, dem serverseitigen Geheimnis und dem Ablaufzeitstempel im Token.

Nachdem Sie die ersten drei Möglichkeiten beseitigt haben, müssen Sie überprüfen, warum der vierte Schritt fehlschlägt. Sie können die Validierung in der flask_wtf/csrf.py-Datei in der validate_csrf()-Funktion debuggen.

Für Ihr Setup müssen Sie überprüfen, ob das Sitzungssetup korrekt ist (insbesondere, wenn Sie nicht die Standardsitzungskonfiguration verwenden) und dass Sie das richtige serverseitige Geheimnis verwenden. Das Formular selbst kann über ein SECRET_KEY-Attribut verfügen, ist jedoch nicht für alle Anforderungen stabil, oder der WTF_CSRF_SECRET_KEY-Schlüssel der App hat sich geändert (letzterer Standardwert ist der app.secret_key-Wert ).

Die CSRF-Unterstützung wurde in Version 0.9.0 hinzugefügt. Schauen Sie sich die spezifische CSRF-Schutzdokumentation an , wenn Sie ein Upgrade durchgeführt haben. Die standardmäßige Flask-WTF Form-Klasse include das CSRF-Token als ausgeblendetes Feld. Das Rendern der verborgenen Felder reicht aus, um es aufzunehmen:

{{ form.hidden_tag() }}
18
Martijn Pieters

Ich fand das Problem schließlich, nachdem ich fast einen Tag daran gearbeitet hatte. :( Vielen Dank an Martijn für seine Hilfe.

Das eigentliche Problem liegt in der Funktionsweise des neuesten flask_wtf.csrf. Die Macher haben es komplett überarbeitet.

Sie müssen alle {{form.hidden_tag()}} in Ihren Vorlagen durch <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/> ersetzen.

Jetzt müssen Sie den CSRF-Schutz explizit aktivieren, indem Sie CsrfProtect(app) hinzufügen.

Die Dokumentation spiegelt dies offensichtlich wider, aber ich wusste nicht, dass sich das geändert hat und Geister gejagt hat.

Es ist ein großes Problem mit der veralteten Funktionalität, ohne den Entwickler irgendwie davon zu benachrichtigen. Jeder, der jetzt auf die neueste Version aktualisiert, wird wie ich Geister verfolgen. Aber es ist auch meine Schuld, dass ich keine Momentaufnahmen meiner Abhängigkeiten gemacht habe. Die Lektion lernte den harten Weg.

15
Houman

Zum Zeitpunkt der Erstellung der App:

from flask_wtf.csrf import CsrfProtect

csrf = CsrfProtect()

app = Flask(__name__)   

...

csrf.init_app(app)

...
0
caverac

Für mich bestand das Problem nicht aus einer schlecht konfigurierten Flask-WTF oder einem fehlenden Token. Es kam aus den Umgebungsvariablen.

Wenn Ihr Flask-Server nicht auf localhost ausgeführt wird, müssen Sie eine SERVER_NAME-Umgebungsvariable festlegen, damit Flask ordnungsgemäß funktioniert. Sie haben wahrscheinlich vergessen, den SERVER_NAME-Wert irgendwo zu ändern.

Zum Beispiel könnten Sie in config/settings.py so etwas sehen:

SERVER_NAME = 'my-domain.com'

Weitere Informationen finden Sie unter diese großartige Ressource

0
louis_guitton