Перейти к содержимому
Назад к пути
Теория 5 мин чтения

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-окружениях.

  1. Хакер отправляет: %2527.
  2. Промежуточный прокси (Nginx) декодирует %25 в знак %, получается %27. Фильтр считает это безопасным.
  3. Ваше Go-приложение декодирует %27 уже в ' (одинарную кавычку).
  4. Опасный символ успешно проникает в 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.

Продолжить чтение

Что бы прочитать модуль полностью, зарегистрируйтесь/войдите на платформу

Когда закончишь — отметь раздел, чтобы продолжить.

🚧 Сайт в разработке. Полный функционал пока недоступен. Все вопросы — support@hackandfix.ru