Безопасность формы: проверка на сервере и спам
Зачем:Форма это единственное место сайта, где чужой текст попадает к вам в базу и в почту. После урока вы знаете, почему проверка в браузере не защита, что проверяет сервер, как остановить спам без капчи, которая мешает людям и делится данными с третьими сторонами, и пишете форму главного действия своего проекта с правилами для сервера.
30 минут
Озвучено синтезом речи (Qwen3-TTS), голос не принадлежит человеку.
Проверка формы на сервере — защита, которую нельзя обойти запросом мимо страницы. Спам останавливают ловушка, лимит частоты и пауза, а не капча.
Единственная дверь
Статический сайт почти ничего не принимает: он только отдаёт. Форма здесь исключение. Через неё в вашу почту, базу и CRM попадает текст чужого человека, а чаще чужой программы. Всё, что приходит снаружи, нужно считать недоверенным, пока вы сами не проверили. Это правило из урока о безопасности (инъекции, права доступа), только теперь оно применено к одному конкретному месту.
В уроке о формах вы уже видели, что required, type="tel" и pattern это удобство для человека, а не защита: запрос, отправленный мимо страницы (скриптом, curl, инструментом разработчика), обходит любую проверку в браузере. Поэтому у формы две проверки, и главная из них серверная.
Проверьте себя: зачем проверять на сервере, если в браузере уже есть required?
Проверка в браузере обходится: запрос можно отправить мимо страницы. Она помогает людям, а серверная защищает вас.
Что проверяет сервер
Принцип один: разрешать известное, а не запрещать плохое. Для каждого поля заранее записано, что в нём допустимо, и всё остальное отклоняется.
- Наличие и тип. Имя есть и это строка; телефон есть.
- Длина. Имя от 2 до 80 знаков, сообщение до 1000. Без верхней границы один запрос может принести мегабайты.
- Формат. Телефон: цифры, плюс, пробелы, скобки, дефисы. Почта: по простому образцу, а настоящую проверку делает письмо подтверждения.
- Лишние поля. Форма отправляет три поля; если пришло шесть, это попытка.
- Размер всего запроса. Ограничен на веб-сервере.
- Файлы (если форма принимает вложения): список разрешённых типов и размер, свой случайный файл-имя, хранение вне публичной папки, и никогда не выполнять. Подробности в памятке OWASP.
- Электронная почта. Если форма отправляет письмо, значения полей в заголовок (тема, «от кого») подставляют только через библиотеку отправки, а переводы строк вычищают: иначе «внедрение заголовков» позволит добавить скрытых получателей, и форма станет рассыльщиком спама.
- Запросы к базе только с параметрами (об этом урок о безопасности), вывод на страницу только с экранированием.
При ошибке сервер отвечает правильным кодом: 400 или 422 «данные неверны», 429 «слишком много запросов», 500 «внутренняя ошибка». И правильным сообщением человеку: «Проверьте номер телефона», «Не удалось отправить, повторите позже». Не нужно показывать внутренности: имена таблиц, пути к файлам и тексты исключений помогают атакующему, а человеку ничего не говорят. Подробности идут в журнал. Объявляйте ошибку диктору через role="alert" (урок про диктор).
И ещё одна оговорка про вход: сообщение «такого пользователя нет» и сообщение «неверный пароль» позволяют перебрать, кто у вас зарегистрирован. Пишите одинаково: «неверный адрес или пароль».
Откуда берётся спам
Три источника: простые боты (заполняют каждое поле и шлют сотни раз), умные боты (настоящий браузер, ведут себя почти как человек) и люди, которым платят за спам. Первых больше всего, и против них хватает недорогих мер.
Меры расположены от самых дешёвых для человека к самым дорогим.
- Поле-ловушка (honeypot, «горшок с мёдом»). Добавляют лишнее поле, которое человек не видит, и называют его так, чтобы бот соблазнился («Сайт», «URL»). Человек его не заполнит, бот заполнит. Если поле заполнено, сервер делает вид, что всё в порядке, и тихо отбрасывает заявку. Здесь есть ловушка для вас самих: скрывать поле нужно так, чтобы оно было скрыто для всех людей. Скрытое только цветом (белое на белом) читает диктор, и менеджер паролей может его заполнить, и честный человек окажется «ботом». Правильно: сдвинуть за край экрана и добавить
aria-hidden="true",tabindex="-1"иautocomplete="off". Аdisplay: noneумные боты обходят. - Проверка времени. В скрытое поле при показе формы кладут отметку времени (подписанную сервером). Форма, отправленная быстрее трёх секунд, не человеческая.
- Ограничение частоты (rate limit). Вы встречали его в уроке о REST API, здесь оно применено к форме: не больше нескольких заявок в час с одного адреса или номера, дальше ответ 429. Это единственная мера, которая останавливает и умных ботов.
- Правила содержимого. Например, не больше двух ссылок в первом сообщении.
- Подтверждение. Для рассылки: письмо со ссылкой, пока человек не нажмёт, адрес не подписан (двойное согласие).
- Капча. Задачка «докажи, что ты не робот». Она мешает людям (читать искажённые буквы трудно, а у части вариантов нет доступной замены, W3C называет это барьером) и обычно передаёт данные об устройстве и поведении стороннему сервису. Это вопрос и к закону о персональных данных, и к доступности (3.3.8). Если капча всё же нужна, берут доступную и без лишнего сбора данных (например, Yandex SmartCaptcha или Cloudflare Turnstile) и называют сервис в политике обработки данных. Иностранный сервис капчи это ещё и вопрос передачи данных за границу, о нём в следующих уроках.
Проверьте на схеме: честные люди и боты, и четыре меры. Особенно посмотрите, что случается с человеком с диктором, если ловушка спрятана только цветом.
Контактная форма и четверо посетителей. Включайте защиту и смотрите, пропускается ли спам и не страдают ли честные люди.
- Человек с мышью
Пропущен. Проходит: поле-ловушку не видит и не трогает, форму заполняет не мгновенно. - Человек с диктором и менеджером паролей
Пропущен. Проходит. - Простой бот: заполняет все поля мгновенно, сотни раз в минуту
Пропущен. Проходит: ничто его не останавливает. - Умный бот: настоящий браузер, обходит скрытые поля, ждёт, но шлёт тридцать заявок в минуту с одного адреса
Пропущен. Проходит.
Спам прошёл: 2 из 2. Честных остановлено: 0 из 2. Часть спама ещё проходит: у каждой меры своя слабость, поэтому их сочетают.
Текстовая альтернатива: схема показывает, что скрытое поле-ловушка останавливает простых ботов, но ловушка, спрятанная только цветом, ловит и человека с диктором. Скрытое за край поле с aria-hidden, tabindex=-1 и autocomplete=off безопасно для людей. Умного бота останавливает лимит частоты, а не ловушка. Обязательная капча чужого сервиса неудобна людям с ограничениями и передаёт данные третьей стороне.
Проверьте себя: почему поле-ловушку нельзя прятать белым цветом на белом?
Его прочитает диктор, до него дойдёт автозаполнение, и честный человек заполнит его и будет принят за бота. Скрывать нужно от всех людей: за край экрана, с aria-hidden, tabindex=“-1” и autocomplete=“off”.
Остальное по списку
- CSRF (подделка межсайтового запроса). Если у человека есть открытый вход на вашем сайте, чужая страница может заставить его браузер отправить запрос от его имени. Защита: токен в форме или cookie с
SameSite=Lax(о нём в уроке об аутентификации) и проверка заголовкаOrigin. Для формы заявки без входа риск мал, а для личного кабинета обязателен. - Куда уходит форма. Для статического сайта форму принимает сервис или своя функция (урок про serverless). Приёмник данных тоже получатель персональных данных: где он расположен и кто им владеет, решает вопрос по закону (следующие уроки). Ключи к приёмнику лежат в переменных окружения, а не в странице.
- CSP форма.
form-action 'self'не позволит подменить адрес отправки вставленной чужой формой. - Проверяйте сами. Отправьте форму пустой, с очень длинным текстом, с кавычкой и скриптом в поле, десять раз подряд. То, что вы не успели проверить, проверит бот. В уроке про тесты вы составляли такие сценарии.
Попробуйте и второй вид схемы: те же меры в ленте попыток, ограниченной формой.
Это обычные автоматические попытки, которые идут на любой сайт. Включайте меры и смотрите, что остановлено.
- Бот перебирает пароли к странице входа в админку
Прошло. Что это по OWASP: A07 Authentication Failures. - В поле «Комментарий» вставлен скрипт, чтобы выполниться у других посетителей
Прошло. Что это по OWASP: A05 Injection (XSS). - В поле формы введена кавычка и фрагмент SQL, чтобы вытащить базу
Прошло. Что это по OWASP: A05 Injection (SQL). - Чужой в открытой сети Wi-Fi читает, что отправляет форма
Прошло. Что это по OWASP: A04 Cryptographic Failures. - Ваш сайт показан невидимым слоем на чужой странице, чтобы подменить клик
Прошло. Что это по OWASP: A02 Security Misconfiguration. - Бот отправляет форму заявки тысячу раз с рекламой
Прошло. Что это по OWASP: A06 Insecure Design.
Остановлено 0 из 6. Пока ничего: сайт открыт всем, кто попробует.
Текстовая альтернатива: лента из попыток (перебор пароля, дыра в плагине, скрипт и SQL в форме, перехват в Wi-Fi, встраивание сайта в чужую страницу, поиск ключей, подмена номера в адресе, спам) и список мер. Каждая попытка останавливается хотя бы одной подходящей мерой: двухфактором или ограничением частоты, обновлением, проверкой ввода, HTTPS, заголовками, хранением секретов вне кода, проверкой прав.
Практика
Вы остаётесь в эпохе 2022 года. Защитите форму главного действия: добавьте ловушку, поля времени, maxlength, уберите стороннюю капчу и запишите правила, которые будет применять сервер.
Проверка читает разметку и текст: она найдёт ловушку, скрытую правильно, required и maxlength, отсутствие сторонней капчи, role="alert" и числа в правилах. Работу сервера она не проверит: настоящий сервер вы будете писать, опираясь на эту таблицу. Её можно показать тому, кто будет делать приёмник: это и есть техническое задание.
Что было не так раньше
Форма 1990-х отправляла письмо скриптом на Perl, который подставлял всё как есть, и «внедрение заголовков» делало такие формы рассыльщиками спама на тысячи адресов. Против спама придумали искажённые буквы (CAPTCHA), потом «я не робот»; они помогли, но разделили людей: часть не могла их пройти, а данные об устройстве уходили стороннему сервису. Сегодня опираются на комбинацию дешёвых мер, которые не мешают человеку, и оставляют капчу на крайний случай.
Практика
Готовлю вашу страницу…
Тест
Вопрос 1 из 6
Карточки
Эти карточки попадут в повторение, когда вы завершите урок.
Почему форму проверяют на сервере?
Ответ
Проверка в браузере обходится: запрос можно отправить мимо страницы. Браузерная проверка помогает людям, серверная защищает вас. Проверять по списку разрешённого: тип, длина, формат, размер, лишние поля.
Как устроена поле-ловушка?
Ответ
Скрытое от людей поле: сдвинуто за край, aria-hidden="true", tabindex="-1", autocomplete="off". Бот заполняет его, и сервер молча отбрасывает заявку. Скрывать только цветом нельзя: диктор прочтёт.
Какие меры против спама не мешают людям?
Ответ
Ловушка, ограничение частоты (с кодом 429), проверка времени, отказ от ссылок в первом сообщении. Капчу добавляют, когда этого мало, и берут доступную и без лишних данных.
Какие сообщения об ошибке показывать?
Ответ
Человеку: что делать («проверьте номер телефона»), без внутренностей. Подробности (запрос, путь, версия) только в журнал. Объявлять ошибку диктору через role="alert".
Что опасно в форме, которая отправляет письмо?
Ответ
Внедрение заголовков: перевод строки в имени добавляет получателей. Вычищайте переводы строк, используйте библиотеку отправки и ограничьте число писем.
Источники
- OWASP: Input Validation Cheat Sheet CC BY-SA, по-английски
- OWASP: Query Parameterization Cheat Sheet CC BY-SA, по-английски
- OWASP: Error Handling Cheat Sheet CC BY-SA, по-английски
- OWASP: Cross-Site Request Forgery Prevention Cheat Sheet CC BY-SA, по-английски
- Client-side form validation (MDN) CC BY-SA, по-английски
- Inaccessibility of CAPTCHA (W3C) только ссылка, по-английски
Дальше
Теперь вы знаете: безопасность форм и спам.