Практические меры защиты PHP-приложений от распространённых уязвимостей и атак

Не передавайте пользователю больше, чем нужно. Если скрипт ожидает ID – отдавайте только ID. Никогда не разрешайте ввод, который может быть интерпретирован как SQL, путь к файлу или PHP-код. Жестко фильтруйте входящие данные: числа – через приведение `(int)`, строки – через `htmlspecialchars()` или `filter_var()` с нужным флагом. Форма должна быть тупой, сервер – параноиком.

Не доверяйте cookie, session и заголовкам. Любой элемент, который приходит от клиента, может быть подделан. Проверка подлинности – только на основе сервера. Хэшируйте всё через `hash_hmac()`, используйте `password_hash()` с алгоритмом `PASSWORD_ARGON2ID` или `PASSWORD_BCRYPT`. Никогда – `md5`, даже для соли. Это уже дырка.

Ошибки – ваш враг. Точнее, их отображение. Оставите `display_errors=On` в продакшене – сдадите структуру каталогов, пароли и SQL-запросы злоумышленнику. Вместо этого включайте логирование (`log_errors=On`, путь до лога вне `public_html`) и используйте обработчики исключений. Даже `try { } catch () {}` даст вам контроль, если написать голову, а не заглушку.

И еще: не храните доступ к БД в открытом `config.php`. Переменные окружения, `dotenv`, шифрование, права доступа к файлам – всё это не опции, а минимальный уровень приличия. Доступ к админке? Только по IP, двухфакторка, случайный путь (`/admin-1a2b3c/`). Все остальное – прямое приглашение на взлом. Или вы так не думаете?

Как предотвращать SQL-инъекции с помощью подготовленных выражений и валидации данных

Не передавайте значения напрямую в SQL-запрос. Никогда. Вместо этого используйте подготовленные выражения (prepared statements) – они разделяют код и данные, исключая возможность внедрения чужого SQL.

Пример на PDO:

$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
$stmt->execute(['email' => $input]);

Переменная $input здесь не попадает внутрь строки запроса напрямую – движок сам экранирует её, не давая вредоносному коду превратиться в команду. Такой подход – не «рекомендация», а базовое требование.

Но этого мало. Даже если используешь bind-параметры, всё равно обязан валидировать входные данные. Например, email не должен просто «выглядеть как email». Проверяй, соответствует ли он регулярному выражению, ограничивай длину, фильтруй недопустимые символы. Зачем? Потому что надежность начинается на входе.

Не вставляй сырые данные в LIMIT или ORDER BY – подготовленные выражения там не работают. Эти части запроса собирай вручную, сравнивая значения с белым списком. Пример:

$order = in_array($input, ['name', 'created_at']) ? $input : 'name';

Фреймворки? Да, они помогают. Но не полагайся вслепую – проверь, что именно делает ORM или query builder. Некоторые из них используют эмуляцию prepared statements по умолчанию – отключай.

И последнее. Никогда не записывай в базу то, что не проверил. Даже если это просто числовой ID, даже если поле скрыто. Клиенту нельзя доверять. Проверяй всё. Всегда.

Никогда не вставляй пользовательский ввод напрямую в HTML, JavaScript или атрибуты. Вообще. Вместо этого – экранируй. Для HTML – `htmlspecialchars()` с флагом `ENT_QUOTES`, для JavaScript – строгое кодирование символов, включая кавычки, слэши, угловые скобки. Пример: `htmlspecialchars($input, ENT_QUOTES | ENT_SUBSTITUTE, ‘UTF-8’)`.

Сырая форма данных с фронта – не повод для доверия. Обязательно проверяй, что ты получил. Ожидаешь email? Прогоняй через `filter_var($value, FILTER_VALIDATE_EMAIL)`. Ждёшь URL? `FILTER_VALIDATE_URL`. Не просто очищай – верифицируй, что ввод соответствует ожидаемому формату. Иначе XSS просочится в щель, где ты не ждёшь.

Опасные места, где легко ошибиться

Вот типичные ловушки: вставка значений в `onclick`, `onerror`, `href`, `src`. Даже `title` может быть точкой входа. Формы? Не менее уязвимы – особенно если ошибки отображаются через user input. Комментарии, сообщения, формы обратной связи – туда XSS вползает первым.

И ещё: не пиши «сам себе фильтр». Не изобретай велосипед на `preg_replace`. Используй отлаженные механизмы. Например, [OWASP PHP Encoder](https://owasp.org/www-project-php-security-project/) или `HTMLPurifier`, если нужно очищать HTML, а не просто текст. У HTMLPurifier есть чёткие правила, whitelisting и защита от вложенных скриптов – просто подключи и настрой под себя.

Если ты используешь шаблонизатор – убедись, что экранирование включено по умолчанию. В `Twig` – это делается автоматически. Но если ты отключил автоэкранирование или используешь « в голом PHP – ты уязвим. Лучше явно: «.

XSS – это не ошибка программиста, это его недосмотр. Автоматизируй, оборачивай, проверяй. И помни: всё, что пришло извне – потенциальный код, а не просто строка.

Источник: https://owasp.org/www-community/xss

Контроль доступа и управление сессиями: предотвращение захвата учетных данных

Отключи передачу идентификаторов сессий через URL. Только cookie. Всегда. Даже одна переданная строка через GET – приглашение для перехвата. Особенно в публичных сетях или при открытых реферерах.

Принудительно задавай флаг HttpOnly для всех cookie сессий. Это блокирует доступ к ним через JavaScript и существенно снижает вероятность XSS-кражи. Добавь флаг Secure, если используется HTTPS – иначе токены могут утечь при любом редиректе.

Никогда не доверяй идентификатору сессии при смене прав или авторизации. При входе пользователя уничтожай текущую сессию и создавай новую. Это обнуляет старые привязки и минимизирует риск фиксации сессии (session fixation).

Контролируй срок жизни сессии. Не сутками, не неделями – максимум 20 минут неактивности. После этого – уничтожение и перенаправление на логин. Активен – обновляй метку времени.

Привязывай сессии к IP и User-Agent. Малейшее расхождение – сброс. Это не даст злоумышленнику использовать перехваченные данные с другого устройства или браузера.

Не допускай слабую авторизацию. Все приватные маршруты должны проверять не только факт входа, но и конкретные роли. Не храни уровень доступа в клиенте. Любое решение на стороне пользователя – дырка.

Добавь защиту от повторного входа с разных мест. Например, одноразовые токены или блокировка предыдущей сессии при новой авторизации. Это спасает при краже пароля, пока пользователь не заметил.

Для административной панели – двухфакторная проверка. Без вариантов. И SMS, и почта легко перехватываются – используй приложения-генераторы токенов (TOTP).

Логи. Подробные. Каждое подозрительное действие: смена IP, странная активность, множественные логины – всё логируй. И не в текстовом файле, а в безопасной базе. Анализируй регулярно.

И самое главное – не используй самописные механизмы сессий. Пользуйся проверенными средствами, например, session_start() в связке с конфигурацией ini_set и корректными заголовками. Изобретения здесь – путь к утечке данных.