Валидация и санитизация ввода

Большинство уязвимостей сводится к одному: программа поверила вводу, который не должна была.

Валидация — проверка, что ввод соответствует ожиданиям (тип, формат, диапазон). Санитизация — приведение ввода к безопасному виду перед использованием.

Два разных действия

Их часто путают, но это разные шаги:

  • Валидация отвечает на вопрос «ожидали ли мы такие данные?». Возраст — это число от 0 до 120? Email похож на email? Если нет — отклоняем.
  • Санитизация отвечает на вопрос «как безопасно это использовать?». Например, экранировать данные перед вставкой в HTML.

Часто нужны оба: сначала проверить, потом безопасно использовать в нужном контексте.

Allowlist надёжнее blocklist

Есть два подхода к проверке. Blocklist (чёрный список) пытается перечислить всё плохое и запретить — но злоумышленник придумает то, чего в списке нет. Allowlist (белый список) описывает, что разрешено, и отвергает всё остальное. Allowlist почти всегда надёжнее, потому что список допустимого конечен и его проще задать точно.

import re

# Allowlist: имя пользователя — только латиница, цифры, _ , длина 3-16
pattern = re.compile(r"^[a-zA-Z0-9_]{3,16}$")

def valid_username(name):
    return bool(pattern.fullmatch(name))

for name in ["alice_42", "hi", "drop table users", "плохой", "ok_name"]:
    print(f"{name!r:20} ->", valid_username(name))

Вывод:

'alice_42'           -> True
'hi'                 -> False
'drop table users'   -> False
'плохой'             -> False
'ok_name'            -> True

Мы разрешили строго определённый набор символов и длину — всё подозрительное отсеялось само, без перечисления «плохого».

Проверять нужно на сервере

Проверки на клиенте (в браузере) — это удобство для пользователя, а не безопасность. Их легко обойти: запрос можно отправить напрямую, минуя интерфейс. Решающая валидация всегда на сервере, где её нельзя обойти. Клиентская проверка — приятный бонус, серверная — обязательна.

Нормализация перед проверкой

Иногда один и тот же ввод можно записать по-разному (разный регистр, лишние пробелы, разные кодировки). Перед проверкой данные стоит привести к единой форме (нормализовать), иначе обходной вариант проскочит мимо валидации. Простой пример — обрезать пробелы и привести email к нижнему регистру перед сравнением.

Связь с предыдущими уязвимостями

УязвимостьЧто её закрывает
SQL-инъекцияпараметризация + валидация типа
XSSэкранирование вывода (санитизация под контекст)
Переполнения, сбоипроверка длины и диапазона

Видно, что валидация и санитизация — сквозная защита: они стоят на пути почти каждой уязвимости из этого курса.

Итог

  • Валидация проверяет, что ввод ожидаемый; санитизация — что его безопасно использовать.
  • Allowlist (что разрешено) надёжнее blocklist (что запрещено).
  • Решающая проверка всегда на сервере — клиентскую легко обойти.
  • Нормализуйте ввод перед проверкой, чтобы не пропустить обходные варианты.
Проверьте себя
1. Чем валидация отличается от санитизации?
AЭто одно и то же
BВалидация проверяет, что ввод ожидаемый; санитизация приводит его к безопасному виду
CСанитизация выполняется только на клиенте
DВалидация всегда необратима
2. Почему allowlist надёжнее blocklist?
AОн короче
BОн описывает конечное множество разрешённого и отвергает всё остальное
CОн работает быстрее
DОн не требует проверки на сервере
3. Где должна выполняться решающая валидация?
AТолько в браузере у пользователя
BНа сервере, где её нельзя обойти
CВ базе данных вместо сервера
DНигде, достаточно типов
4. Зачем нормализовать ввод перед проверкой?
AЧтобы ускорить запрос
BЧтобы один и тот же ввод в разных формах не проскочил мимо проверки
CЧтобы зашифровать его
DЭто не нужно
Поддержать проект