web-dev-qa-db-fra.com

La validation du formulaire échoue car CSRF est manquant.

Il y a quelques jours, j'ai réinitialisé mon environnement de flacon local sans avoir capturé les dépendances via un pip freeze avant de le supprimer. Je devais donc réinstaller la dernière version de la pile entière.

Désormais, je ne suis plus capable de valider avec des formulaires. Flacon affirme que CSRF serait manquant.

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

La première fois que j'envoie une Get, je récupère un form.errors vide comme prévu ..__ maintenant, je remplis le formulaire et le soumets, et form.errors affiche: {'csrf_token': [u'CSRF token missing']}

C'est si étrange. Je me demande si Flask-WTF a changé et que je ne l’utilise pas correctement.

Je peux clairement voir que le form.CSRF_token existe, alors pourquoi prétend-il qu'il était manquant?

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

Je n'ai jamais touché le modèle de travail, mais je le poste néanmoins:

{% 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 %}

Est-ce un nouveau bug?

METTRE À JOUR:

J'ai tout réinstallé et le problème persiste. 

Comme Martijn l'a suggéré, je suis en train de déboguer dans la méthode suivante dans 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'))

La dernière condition est à l'origine de l'erreur de validation.

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

Et vous aviez raison, la comparaison HMAC échoue ... les deux valeurs sont à chaque fois différentes.

return hmac_compare == hmac_csrf

J'ai les deux SECRET_KEY et CSRF_SESSION_KEY définis dans ma configuration.

18
Houman

L'infrastructure Flask-WTF CSRF rejette un jeton si:

  • le jeton est manquant. Pas le cas ici, vous pouvez voir le jeton dans le formulaire.

  • il est trop ancien (l'expiration par défaut est définie sur 3600 secondes, ou une heure). Définissez l'attribut TIME_LIMIT sur les formulaires pour remplacer cela. Probablement pas le cas ici.

  • si aucune clé 'csrf_token' n'est trouvée dans la session en cours. Apparemment, vous pouvez voir le jeton de session, donc c'est également le cas.

  • Si la signature HMAC ne correspond pas; la signature est basée sur la valeur aléatoire définie dans la session sous la clé 'csrf_token', le secret côté serveur et l'horodatage d'expiration dans le jeton.

Après avoir éliminé les trois premières possibilités, vous devez vérifier pourquoi la 4ème étape échoue. Vous pouvez déboguer la validation dans le fichier flask_wtf/csrf.py, dans la fonction validate_csrf().

Pour votre configuration, vous devez vérifier que la configuration de la session est correcte (en particulier si vous n'utilisez pas la configuration de session par défaut) et que vous utilisez le secret côté serveur correct. Le formulaire lui-même peut avoir un attribut SECRET_KEY défini mais n'est pas stable d'une requête à l'autre, ou la clé app WTF_CSRF_SECRET_KEY a été modifiée (la dernière par défaut est app.secret_key valeur ).

La prise en charge de CSRF a été ajoutée à la version 0.9.0, vérifiez le documentation de protection CSRF si vous avez effectué une mise à niveau. La classe standard Flask-WTF Form class inclut le jeton CSRF en tant que champ masqué, le rendu des champs masqués suffit à l'inclure:

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

J'ai finalement trouvé le problème après presque une journée de travail. :( Un grand merci à Martijn pour son aide.

Le problème actuel réside dans le fonctionnement du dernier flask_wtf.csrf. Les décideurs ont complètement repensé.

Vous devez remplacer tous les {{form.hidden_tag()}} dans vos modèles par <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>.

Et vous devez maintenant activer la protection CSRF explicitement en ajoutant CsrfProtect(app).

La documentation reflète maintenant évidemment cela, mais je ne savais pas que cela avait changé et chassait les fantômes.

C’est un gros problème de fonctionnalité déconseillée sans que le développeur en soit averti. Tous ceux qui passeront maintenant à la dernière version, chasseront les fantômes comme je l’ai fait. Mais c'est aussi ma faute de ne pas avoir pris un instantané de mes dépendances. Leçon apprise à la dure.

15
Houman

Au moment de créer l'application:

from flask_wtf.csrf import CsrfProtect

csrf = CsrfProtect()

app = Flask(__name__)   

...

csrf.init_app(app)

...
0
caverac

Pour moi, le problème ne venait pas de Flask-WTF mal configuré, ni d'un jeton manquant. Il venait des variables d'environnement.

Si votre serveur Flask ne s'exécute pas sur localhost, vous devez définir une variable d'environnement SERVER_NAME pour que Flask fonctionne correctement. Vous avez probablement oublié de modifier la valeur SERVER_NAME quelque part.

Par exemple, vous pourriez avoir quelque chose comme ceci dans config/settings.py:

SERVER_NAME = 'my-domain.com'

Pour plus d'informations, consultez cette excellente ressource

0
louis_guitton