Flask-WTF, WTForms и защита от CSRF
Flask-WTF превращает форму в класс Python: поля, валидаторы и CSRF-защита описываются декларативно, а view-функция становится короткой и чистой.
Ручная валидация быстро разрастается. Flask-WTF собирает правила формы в одном классе: объявил поля и валидаторы — и метод validate_on_submit сам проверит данные и защитит от CSRF. View сжимается до «создать форму, проверить, сохранить, редиректнуть».
Flask-WTF — обёртка над библиотекой WTForms. Форма описывается классом, наследующим FlaskForm; поля — это атрибуты класса с типом и списком валидаторов. Это декларативный стиль: ты описываешь что должно быть верным, а не как проверять.
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Email, Length
class RegisterForm(FlaskForm):
name = StringField("Имя", validators=[DataRequired()])
email = StringField("Email", validators=[DataRequired(), Email()])
password = PasswordField("Пароль", validators=[Length(min=8)])
Во view форма создаётся и проверяется методом validate_on_submit — он истина только если запрос POST, форма валидна и CSRF-токен верный:
@app.route("/register", methods=["GET", "POST"])
def register():
form = RegisterForm()
if form.validate_on_submit():
# form.name.data, form.email.data ...
return redirect(url_for("success"))
return render_template("register.html", form=form)
CSRF (подделка межсайтовых запросов) — атака, где чужой сайт втайне отправляет запрос от имени залогиненного пользователя. Flask-WTF вставляет в форму скрытый токен и проверяет его при сабмите. Для этого обязателен SECRET_KEY — им подписывается токен.
Декларативный стиль Flask-WTF — это про то, чтобы правила формы жили в одном месте и были видны с первого взгляда. Вместо россыпи ручных if по полям ты объявляешь класс: вот поля, вот их валидаторы. View после этого сжимается до канонической четвёрки строк — создать форму, validate_on_submit, выполнить бизнес-логику, редирект. Бонусом идёт автоматическая защита от CSRF, которую вручную реализовать правильно непросто. Стоит помнить и о границах применимости: Flask-WTF заточен под классические HTML-формы; если строишь чистый JSON-API, форму с CSRF-токеном обычно заменяют на валидацию тела через Pydantic или Marshmallow и другую защиту от подделки запросов.
Как работает под капотом
CSRF-защита работает так: при рендере формы сервер кладёт в неё уникальный подписанный токен и запоминает его в сессии. При сабмите токен из формы сверяется с сохранённым. Чужой сайт токена не знает — его запрос отклоняется.
GET /register
│ сервер генерирует токен T, кладёт в форму и в сессию
▼
<input type=hidden name=csrf_token value=T>
│ пользователь сабмитит
▼
POST: токен из формы == токен в сессии?
├─ да → форма принята
└─ нет → 400, запрос отклонён (защита от CSRF)
Смоделируем проверку токена обычным Python.
import secrets
session = {}
def render_form():
token = secrets.token_hex(8)
session["csrf"] = token
return token # уходит в скрытое поле формы
def submit(form_token):
expected = session.get("csrf")
if form_token != expected:
return "400 CSRF mismatch"
return "200 принято"
t = render_form()
print(submit(t)) # верный токен
print(submit("подделка")) # чужой сайт не знает токен
Запусти: запрос с верным токеном принимается, с поддельным — отклоняется. Так Flask-WTF отсекает подделанные межсайтовые запросы автоматически для каждой формы.
Частые ошибки
- Не задать SECRET_KEY. Без него CSRF-защита не работает, а Flask-WTF ругается.
- Забыть {{ form.csrf_token }} в шаблоне. Без скрытого токена сабмит провалится с 400.
- Проверять request.method вместо validate_on_submit. Метод уже включает проверку метода, валидности и CSRF.
Best practices
- Держи SECRET_KEY в переменных окружения, не в коде.
- Защищай CSRF все формы, меняющие состояние.
- Правила валидации — в классе формы, view оставляй тонким.
Что запомнить
- Flask-WTF описывает форму классом с полями и валидаторами.
- validate_on_submit проверяет: POST + валидность + CSRF-токен.
- Скрытый подписанный csrf_token защищает от подделки межсайтовых запросов.
- Для работы CSRF обязателен SECRET_KEY; держи его вне кода.
Итог: Flask-WTF описывает форму классом с валидаторами, validate_on_submit делает всю проверку, а скрытый подписанный токен защищает от CSRF (нужен SECRET_KEY). Дальше — сессии, куки и flash-сообщения.