2. Игры с кодировками (Hex, Unicode)
Игры с кодировками: Как спрятать SQL-инъекцию
Кодирование (Encoding) для разработчика — это всего лишь способ представления данных. Для хакера же это идеальный «камуфляж». Символы и команды, такие как UNION или SELECT, можно трансформировать в шестнадцатеричный код или альтернативные представления так, чтобы автоматические фильтры безопасности не распознали в них угрозу.
Каждый HTTP-запрос в Go-приложении проходит через серию декодеров: сначала браузер кодирует символы в URL-form (%20 для пробела, %27 для апострофа), потом стандартная библиотека net/http декодирует их при парсинге r.URL.Query() или r.ParseForm(). Если перед Go стоит reverse-proxy типа Nginx или HAProxy — у них есть свой decoder. Если приложение принимает JSON — json.Unmarshal может дополнительно декодировать \uXXXX escape-последовательности. На каждом из этих этапов представление данных меняется, и если фильтр на безопасность стоит НЕ в самом последнем звене перед SQL-парсером, он работает с устаревшей версией строки. Это называется «impedance mismatch» — несовпадение интерпретаций между слоями стека.
Аналогия: представь таможню, которая проверяет посылки на запрещённое слово «бомба». Отправитель пишет на коробке «бомба» через два слоя: внешняя обёртка зашифрована шифром Цезаря (срнва), внутренняя — чистый текст. Таможенник видит «срнва», в его базе такого слова нет, посылка пропускается. На месте получения коробку расшифровывают — внутри явно опасное содержимое. Decoder применяется ПОСЛЕ фильтра, и это всегда выигрышная позиция для атакующего.
Изучим три основных способа кодирования вредоносных SQL-нагрузок, и в конце вернёмся к единственному способу защиты, который не зависит от того, сколько раз и в каком порядке данные декодируются по пути от браузера до базы.
1. Механика: Hex-кодирование (MySQL / SQLite)
База данных оперирует не только текстом, но и числами. Хакер может заменить строковое значение его шестнадцатеричным эквивалентом. MySQL и SQLite принимают синтаксис 0x... как литерал, эквивалентный соответствующей ASCII-строке. PostgreSQL чуть строже — там нужна явная конструкция decode('616461696e', 'hex'), но идея та же: представить строку в форме, которую регулярка 'admin' не распознает.
Пример:
WHERE name = 0x61646d696e (что соответствует строке admin)
Если ваш Go-фильтр ищет в запросе слово 'admin', он проигнорирует последовательность 0x616.... Но MySQL воспримет это число как легитимную строку, и хакер получит доступ к данным, спрятавшись за числовым кодом.
Расширение приёма: внутри одного payload можно комбинировать hex-литералы с конкатенацией. Например, CONCAT(0x61, 0x64, 0x6d, 0x69, 0x6e) в MySQL вернёт строку admin, и каждый отдельный байт по отдельности выглядит совершенно безобидно. Атакующие используют это против фильтров, которые блокируют слово admin целиком, но пропускают отдельные числа. То же самое можно сделать через MySQL-функцию CHAR(97,100,109,105,110) — CHAR принимает целые числа и возвращает соответствующие ASCII-символы.
2. URL-Encoding и Двойное кодирование
Запрос проходит через цепочку серверов (например, Nginx → Go Fiber → PostgreSQL), и каждый из них может по-своему интерпретировать кодировку. Это классическая «double encoding» атака — техника, описанная в OWASP Testing Guide ещё с 2008 года, но до сих пор успешно работающая против WAF и Web Application Firewall в production-окружениях.
- Хакер отправляет:
%2527. - Промежуточный прокси (Nginx) декодирует
%25в знак%, получается%27. Фильтр считает это безопасным. - Ваше Go-приложение декодирует
%27уже в'(одинарную кавычку). - Опасный символ успешно проникает в SQL-запрос.
Аналогичный приём работает и с Unicode-кодированием. JavaScript-фронтенд может отправить символ как ' (одинарная кавычка в JSON-форме), json.Unmarshal в Go декодирует это в реальный апостроф, и если фильтр стоял на JSON-уровне (например, искал подстроку '), он ничего не нашёл. UTF-8 multi-byte encoding ещё интереснее: символ ' можно представить как %C0%A7 — это «overlong UTF-8», технически невалидная последовательность, но некоторые старые парсеры её принимают и декодируют как обычный апостроф. CVE-2002-1234 и серия похожих багов в Apache mod_security именно об этом.
3. Таблица работы: Кодировки vs Фильтры
| Обычный SQL | Закодированный вариант | Результат в СУБД |
|---|---|---|
'admin' |
CHAR(97,100,109,105,110) |
'admin' |
UNION |
un%49on |
UNION |
' |
%27 или \x27 |
' |
4. Почему проверки в коде не всегда эффективны
Даже использование стандартных функций вроде url.QueryUnescape не гарантирует полной очистки. Существуют специфические кодировки СУБД (например, использование Unicode-символов, визуально похожих на кавычки), которые Go-валидатор пропустит, а SQL-парсер интерпретирует как управляющие символы.
Реальный пример: символ U+2018 (LEFT SINGLE QUOTATION MARK, типографская «открывающая» кавычка) визуально почти неотличим от обычного апострофа U+0027, но проходит через strings.Contains(s, "'"). Если ваше Go-приложение нормализует ввод через unicode/norm пакет с формой NFKC, многие типографские символы автоматически конвертируются в их ASCII-эквиваленты — и инъекция становится валидной. Похожая история с символами Cuneiform Numeric Sign One (U+12415) и сотней других «цифроподобных» символов, которые проходят через regex \d+ и потом превращаются в реальные цифры на этапе нормализации.
Типовая ошибка разработчика: накатить «универсальный фильтр» через regexp.MustCompile со списком из 50 паттернов и считать, что вектор закрыт. Каждый месяц исследователи находят новый bypass — добавление нового паттерна в фильтр превращается в Sisyphean task. Эта стратегия (blacklisting) принципиально проигрышная; в следующем уроке мы разберём, почему whitelisting тоже не панацея, но хотя бы более устойчив.
Мир кодировок — это бесконечный лабиринт. Единственный способ не заблудиться в нем — полностью отказаться от конкатенации строк при работе с БД. Если вы используете плейсхолдеры (? или $1), то любая, даже самая закодированная нагрузка, останется для базы данных просто «куском текста», лишенным власти.
Архитектурный принцип за этим: Prepared Statements работают на уровне сетевого протокола между Go-драйвером и СУБД. Сначала отправляется текст шаблона SELECT ... WHERE name = ?, СУБД парсит и компилирует его в план выполнения. Потом отдельным сообщением передаются bind-параметры — это бинарные значения, не текст SQL. На этой стадии никакая обфускация уже не имеет смысла: СУБД не парсит значение как SQL-токен, оно ложится прямо в WHERE-клаузу как «магическое число» из плана. Hex, double-encoding, Unicode-кавычки — всё это превращается в безобидные байты, которые сравниваются с колонкой как блоб.
Стандарты и соответствие: OWASP Top 10 A03:2021 Injection (этот класс атак сохраняет позицию #3 уже четвёртое издание подряд); CWE-89 SQL Injection (Top 25 Most Dangerous Software Weaknesses); PCI-DSS 6.5.1; GDPR Article 32 (Security of Processing) — нарушение через SQLi с эксфильтрацией персональных данных гарантирует штраф до 4% годового оборота. British Airways в 2019 году получили штраф £20 миллионов от ICO именно за инцидент с эксфильтрацией данных через инъекционную уязвимость.
В следующем уроке мы разберем еще один хитрый прием — как хакеры обходят запреты на пробелы, используя альтернативные разделители SQL.
Продолжить чтение
Что бы прочитать модуль полностью, зарегистрируйтесь/войдите на платформу
Когда закончишь — отметь раздел, чтобы продолжить.