Где происходит фильтрация и валидация данных в приложении?

Ответ

Фильтрация (санация) и валидация данных должны происходить на нескольких уровнях для обеспечения безопасности и целостности данных (Defense in Depth).

1. Уровень представления (клиентская сторона):

  • HTML5-валидация (required, type="email", pattern) для улучшения UX.
  • JavaScript-валидация для мгновенной обратной связи.
  • Важно: Это ненадежно, так как легко отключается, и служит только для удобства пользователя.

2. Уровень транспорта (сервер, точка входа): Это ключевой уровень. Входящие данные (HTTP-запросы) должны быть проверены и очищены перед передачей в бизнес-логику.

  • Валидация: Проверка формата, обязательности, диапазонов, корректности бизнес-правил.
  • Фильтрация: Очистка от потенциально опасных символов (XSS).

    // Пример с использованием Symfony Validator и санитизатора
    class UserRegistrationData
    {
        #[AssertNotBlank]
        #[AssertEmail]
        private string $email;
    
        #[AssertNotBlank]
        #[AssertLength(min: 8, max: 50)]
        #[AssertRegex(pattern: '/^(?=.*[a-z])(?=.*[A-Z])(?=.*d).+$/')]
        private string $password;
    }
    
    // В контроллере
    $data = new UserRegistrationData();
    $data->email = filter_var($request->get('email'), FILTER_SANITIZE_EMAIL);
    $data->password = $request->get('password'); // Пароли не санитизируют
    
    $errors = $validator->validate($data);
    if (count($errors) > 0) { ... }

3. Уровень бизнес-логики (доменный слой):

  • Инварианты предметной области проверяются внутри сущностей (Entities) или доменных сервисов.
    class Order {
        private function __construct(private float $amount) {
            if ($amount <= 0) {
                throw new DomainException('Order amount must be positive.');
            }
        }
    }

4. Уровень persistence (база данных):

  • Использование подготовленных выражений (Prepared Statements) для защиты от SQL-инъекций.
  • Ограничения на уровне схемы БД (NOT NULL, UNIQUE, CHECK).

5. Уровень вывода (View/Template):

  • Экранирование (escaping) всех пользовательских данных при выводе в HTML, JSON, XML для защиты от XSS.
    // В шаблоне Twig экранирование включено по умолчанию
    <p>Welcome, {{ user.username }}!</p>

Итог: Валидация и фильтрация — это непрерывный процесс, а не одноразовое действие в контроллере.

Ответ 18+ 🔞

Да ты послушай, как эту всю кухню безопасности выстраивать надо, а то некоторые думают, что один htmlspecialchars() и — всё, приехали. Нет, чувак, тут слоёв, как в хорошем торте, овердохуища.

Первый слой — это лицо для лохов (клиентская сторона). Ты там на фронте навешаешь required, type="email" — пользователю удобно, сразу видно, где он накосячил. JavaScript'ом подхватишь, мгновенную отдачу дашь. Но это всё, ёпта, мартышлюшка! Любой школьник в DevTools это отключит и пошлёт тебе в запросе что угодно. Доверия к этому — ноль, ебать. Только для уюта.

Второй слой — это серьёзно, въездной контроль (сервер). Вот тут уже терпения ноль, ебать, и волнение ебать начинается. Каждый запрос, как подозрительный тип на границе: его надо обыскать, проверить документы и отмыть, если надо.

  • Валидация: А че там по паспорту? Формат почты верный? Пароль не из трёх символов? Возраст не отрицательный? Бизнес-правила не нарушает?
  • Фильтрация (Санация): А не принёс ли он в кармане гвоздь (скрипт на XSS)? Выковыриваем.
// Допустим, на Symfony делаем. Создаём объект-контейнер для данных.
class UserRegistrationData
{
    #[AssertNotBlank] // Не пустое, бля!
    #[AssertEmail] // Это реально почта?
    private string $email;

    #[AssertNotBlank]
    #[AssertLength(min: 8, max: 50)] // Не меньше восьми, не длиннее полтинника
    #[AssertRegex(pattern: '/^(?=.*[a-z])(?=.*[A-Z])(?=.*d).+$/')] // Чтоб и буквы, и цифры, ядрёна вошь!
    private string $password;
}

// В контроллере принимаем
$data = new UserRegistrationData();
// Почту чистим от мусора
$data->email = filter_var($request->get('email'), FILTER_SANITIZE_EMAIL);
// Пароль НЕ чистим! Иначе он "123&<script>" превратится в "123", и пользователь потом не зайдёт. Храним как есть, но хэшируем.
$data->password = $request->get('password');

// А теперь валидатор долбит этот объект как прокурор
$errors = $validator->validate($data);
if (count($errors) > 0) {
    // Всё, чувак, иди отсюда, документы не в порядке.
}

Третий слой — внутренние правила завода (бизнес-логика). Допустим, данные прошли КПП. Но у тебя в домене свои законы. Заказ не может быть на ноль или отрицательную сумму. Это проверяют сами сущности.

class Order {
    private function __construct(private float $amount) {
        if ($amount <= 0) {
            // Какого хуя? Заказ на минус десять рублей?
            throw new DomainException('Order amount must be positive.');
        }
    }
}

Четвёртый слой — сейф (База данных). Всё, данные чистые и правильные. Теперь надо их сохранить так, чтобы сейф не взломали. Подготовленные выражения (Prepared Statements) — это наш бронежилет от SQL-инъекций. Плюс сама схема БД (NOT NULL, UNIQUE) — последний рубеж, если кто-то проскочил.

Пятый слой — упаковка на выход (Шаблоны). Ты данные из БД достал, теперь выводишь пользователю. СТОП! А вдруг там в имени пользователя, которое он когда-то ввёл, был скрипт? Мы его на втором слое могли и пропустить, если это было разрешено (тип, никнейм). Поэтому при выводе ВСЕГДА экранируем. В Twig, к счастью, это по умолчанию.

// Twig сам всё заэкранирует. Без паники.
<p>Welcome, {{ user.username }}!</p> // Если в username был "<script>", то увидим текст, а не скрипт.

Итог, чувак: Это не "поставил валидатор в контроллере и свободен". Это непрерывный конвейер, где на каждом этапе своя проверка. Называется Defense in Depth — глубокая эшелонированная оборона. Одна защита сломалась — есть вторая, третья. Так и живём, блядь.