«Хороший сайт делается для людей, а поисковики приходят следом»

— из принципов редакции SeoLiga.ru
Главная » Статьи » Веб-программирование » Защита сайта от SQL-инъекций и XSS

Защита сайта от SQL-инъекций и XSS

SQL-инъекции и XSS остаются самыми частыми дырами в веб-приложениях. Причина проста: обе уязвимости возникают из одной и той же ошибки — слепого доверия к данным, пришедшим от пользователя. Сайт принимает строку из формы или адреса и без проверки подставляет её в запрос к базе или в страницу. Злоумышленнику остаётся лишь подобрать нужные символы.

В этой статье разберём, как работают эти атаки, как выглядит уязвимый код и, главное, как его исправить. Примеры будут на PHP и SQL, но принципы одинаковы для любого языка и платформы.

Как работает SQL-инъекция

Представьте страницу, которая показывает товар по его идентификатору из адреса. Разработчик пишет запрос, склеивая строку:

$id = $_GET['id'];
$sql = "SELECT * FROM products WHERE id = " . $id;
$result = mysqli_query($link, $sql);

Если в адрес подставить обычное число, всё работает. Но что будет, если вместо числа передать фрагмент SQL? Например, добавить условие, которое всегда истинно, либо присоединить второй запрос с выборкой пользователей и паролей. База выполнит всё, что получила, потому что не различает, где код разработчика, а где данные пользователя.

Последствия бывают разными:

  • чтение чужих данных: учётные записи, заказы, переписка;
  • обход авторизации без знания пароля;
  • изменение и удаление записей;
  • в тяжёлых случаях запись файлов на сервер и выполнение команд.

Основная защита от SQL-инъекций: подготовленные запросы

Единственный надёжный способ — отделить код запроса от данных. Это делают подготовленные выражения с параметрами. Сначала в базу отправляется шаблон запроса с местами под значения, потом отдельно передаются сами значения. База воспринимает их строго как данные и никогда не исполняет.

$stmt = $pdo->prepare('SELECT * FROM products WHERE id = :id');
$stmt->execute([':id' => $id]);
$row = $stmt->fetch();

В C# аналог выглядит так:

using var cmd = new SqlCommand(
    "SELECT * FROM Products WHERE Id = @id", connection);
cmd.Parameters.AddWithValue("@id", id);

Запомните правило: никогда не вставляйте пользовательские данные в текст запроса конкатенацией. Это относится ко всем источникам: параметрам адреса, формам, куки, заголовкам, данным из файлов и других систем.

Дополнительные меры против SQL-инъекций

  1. Проверка типов. Если ожидается число, приведите значение к целому и отбросьте всё остальное.
  2. Белые списки. Имена полей для сортировки или таблиц нельзя передать параметром. Сопоставляйте ввод с заранее заданным списком допустимых значений.
  3. Минимальные права. Пользователь базы, под которым работает сайт, не должен иметь права удалять таблицы, читать системные данные или писать файлы.
  4. Скрытие ошибок. Не показывайте посетителям тексты ошибок базы: они подсказывают структуру запроса. Пишите ошибки в журнал.
  5. Использование ORM. Современные библиотеки по умолчанию формируют параметризованные запросы, но следите за местами, где вы вставляете сырой SQL.
  6. Хранение паролей в виде хешей. Даже если данные утекут, пароли не прочитаются напрямую. Применяйте современные алгоритмы с солью.

Как работает XSS

Межсайтовый скриптинг происходит, когда сайт выводит в страницу данные пользователя без обработки, и браузер воспринимает их как часть кода. Классический пример — форма комментария:

echo "<div class='comment'>" . $_POST['text'] . "</div>";

Если автор комментария вставит в текст тег скрипта, он выполнится у каждого, кто откроет страницу. Злоумышленник может украсть данные сессии, подменить содержимое страницы, перенаправить на фишинговый сайт, выполнить действия от имени посетителя.

Различают три вида XSS:

  • Отражённый. Вредоносный код приходит в параметрах адреса и сразу выводится на страницу. Жертву заманивают по специальной ссылке.
  • Хранимый. Код сохраняется на сервере, например в комментарии или профиле, и срабатывает у всех, кто открывает страницу. Самый опасный вид.
  • На стороне клиента. Уязвимость в самом скрипте страницы, когда данные из адреса вставляются в документ без обработки.

Основная защита от XSS: экранирование при выводе

Все данные, выводимые в HTML, нужно превращать в безопасный текст: символы, имеющие особый смысл, заменяются сущностями. Тогда браузер покажет их как обычные символы, а не выполнит.

echo htmlspecialchars($text, ENT_QUOTES, 'UTF-8');

Важный принцип: экранирование зависит от контекста вывода. Для HTML-текста, значения атрибута, содержимого скрипта и адреса применяются разные правила. Нельзя защитить все контексты одной функцией. Используйте шаблонизаторы, которые экранируют вывод по умолчанию, и отключайте экранирование только осознанно.

Дополнительные меры против XSS

  • Политика безопасности контента. Заголовок, который ограничивает, откуда можно загружать и выполнять скрипты. Сильно снижает ущерб даже при найденной дыре.
  • Флаг HttpOnly для кук. Скрипт на странице не сможет прочитать идентификатор сессии.
  • Флаги Secure и SameSite. Ограничивают передачу кук и защищают от подделки запросов.
  • Очистка разрешённого HTML. Если вы допускаете форматирование в комментариях, пропускайте текст через проверенную библиотеку очистки с белым списком тегов и атрибутов. Не пытайтесь писать фильтры регулярными выражениями.
  • Проверка адресов. Не допускайте в ссылках схемы, исполняющие код, разрешайте только безопасные протоколы.
  • Безопасное API в браузерном коде. Для вставки текста используйте свойства, работающие с текстом, а не с разметкой.

Типичные ошибки разработчиков

  • Фильтрация на стороне клиента вместо серверной. Запрос можно отправить в обход формы.
  • Попытка «вычистить» опасные слова. Всегда найдётся обходной вариант написания.
  • Экранирование при сохранении, а не при выводе. Данные могут потом пойти в другой контекст.
  • Доверие к данным из внутренних источников: файлы, другие сервисы, база, куда писал кто-то ещё.
  • Использование устаревших функций для работы с базой без параметров.
  • Отображение подробных ошибок на боевом сервере.

Как проверить свой сайт

Проведите хотя бы базовую проверку.

  1. Пройдитесь по всем формам и параметрам адреса и введите одиночную кавычку. Если появляется ошибка базы, перед вами потенциальная инъекция.
  2. Вставьте в поля комментариев и поиска безобидный тестовый тег и посмотрите, как он выводится. Если отображается как разметка, а не как текст, есть проблема.
  3. Используйте автоматические сканеры уязвимостей на тестовой копии сайта, а не на рабочей.
  4. Проведите ревизию кода: найдите места, где в запросы подставляются переменные конкатенацией, и места, где вывод идёт без экранирования.
  5. Настройте журналы и оповещения о подозрительных запросах.

Общая гигиена безопасности

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

Заключение

SQL-инъекции и XSS побеждаются дисциплиной, а не хитрыми фокусами. Используйте параметризованные запросы везде, где в запрос попадают внешние данные, экранируйте всё, что выводите в страницу, ограничивайте права и включайте дополнительные защитные заголовки. Эти простые правила закрывают подавляющее большинство атак. Потратьте время сейчас: восстановление после взлома обойдётся намного дороже и по деньгам, и по репутации.

Р
Редакция
Автор SeoLiga.ru. Пишет о практике: что работает на реальных проектах, а что лучше обойти стороной. Хотите предложить свою статью? Загляните в раздел «Авторам».