Ошибка проверки формы из-за отсутствия CSRF
Несколько дней назад у меня есть reset моя локальная фляга без захвата зависимостей через pip freeze
, прежде чем я удалю ее. Поэтому мне пришлось переустановить последнюю версию всего пакета.
Теперь, когда я сижу, я больше не могу проверять формы. Заявки на фляжи CSRF будут отсутствовать.
def register():
form = RegisterForm()
if form.validate_on_submit():
...
return make_response("register.html", form=form, error=form.errors)
При первом отправке Get
я получаю пустой form.errors
, как и ожидалось.
Теперь я заполняю форму и отправлю ее, а form.errors
показывает: {'csrf_token': [u'CSRF token missing']}
Это так странно. Интересно, изменилась ли Flask-WTF, и я неправильно ее использую.
Я отчетливо вижу, что существует form.CSRF_token
, так почему он утверждает, что он отсутствует?
CSRFTokenField: <input id="csrf_token" name="csrf_token" type="hidden" value="1391278044.35##3f90ec8062a9e91707e70c2edb919f7e8236ddb5">
Я никогда не касался рабочего шаблона, но я размещаю его здесь тем не менее:
{% 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 %}
Является ли это новой ошибкой?
UPDATE:
Я переустановил все, и проблема не устранена.
Как предположил Мартийн, я отлаживаю следующий метод в 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'))
Последнее условие вызывает ошибку проверки.
field.data = "1391296243.8##1b02e325eb0cd0c15436d0384f981f06c06147ec"
self.SECRET_KEY = None (? Is this the problem)
self.TIME_LIMIT = 3600
И вы были правы, сравнение HMAC не удалось.... оба значения в каждый раз разные.
return hmac_compare == hmac_csrf
У меня есть как SECRET_KEY, так и CSRF_SESSION_KEY в моей конфигурации.
Ответы
Ответ 1
Инфраструктура CSRF Flask-WTF отклоняет токен, если:
-
отсутствует токен. Не здесь, вы можете увидеть токен в форме.
-
он слишком старый (по умолчанию для истечения срока действия установлено 3600 секунд или час). Установите атрибут TIME_LIMIT
для форм, чтобы переопределить это. Вероятно, здесь не так.
-
если в текущем сеансе отсутствует ключ 'csrf_token'
. По-видимому, вы можете видеть токен сеанса, так что тоже.
-
Если подпись HMAC не соответствует; подпись основана на случайном значении, установленном в сеансе под ключом 'csrf_token'
, секретной стороне сервера и временной меткой истечения срока действия в токене.
Устраняя первые три возможности, вам нужно проверить, почему 4-й шаг выходит из строя. Вы можете отладить проверку в файле flask_wtf/csrf.py
в функции validate_csrf()
.
Для вашей установки вам необходимо убедиться, что настройка сеанса верна (особенно если вы не используете конфигурацию сеанса по умолчанию) и что вы используете правильную секретность на стороне сервера. Сама форма может иметь атрибут SECRET_KEY
, но нестабильна для всех запросов, или изменилось приложение WTF_CSRF_SECRET_KEY
(последнее по умолчанию соответствует значению app.secret_key
).
Поддержка CSRF была добавлена в версии 0.9.0, если вы обновили ее, просмотрите конкретную документацию по защите CSRF. Стандартный класс Flask-WTF Form
включает токен CSRF в качестве скрытого поля, при этом скрытые поля достаточно для его включения:
{{ form.hidden_tag() }}
Ответ 2
Наконец-то я нашел проблему после почти дня работы над ней.:(
Большое спасибо Мартину, хотя за его помощь.
Реальная проблема заключается в том, как работает последний flask_wtf.csrf
. Изготовители полностью отреагировали на это.
Вы должны заменить все {{form.hidden_tag()}}
в своих шаблонах
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
.
И теперь вы должны включить защиту CSRF явно, добавив CsrfProtect(app)
.
Документация теперь явно отражает это, но я не знал, что это изменилось и преследовало призраков.
Это большая проблема с устаревшей функциональностью, не уведомляя разработчика как-то. Любой, кто обновляется до последней версии, будет преследовать призраков, как я. Но это также моя вина, не сделав снимок моих зависимостей. Урок усвоил трудный путь.