2. Индикаторы истинности (200 vs 404)
Индикаторы истинности: 200 OK vs 404 Not Found
Индикатором истинности (Truth Indicator) может быть любое заметное изменение в ответе сервера, по которому хакер понимает — было ли верно внедренное в SQL-запрос условие. Даже если вы скрываете детали ошибок, само поведение вашего Go-приложения может сообщать результат через HTTP-статусы или текст на странице.
Это и есть суть Blind SQL Injection: атакующий не видит ни строк результата, ни текста SQL-ошибок, но видит как меняется поведение приложения. Аналогия — медвежатник, который слушает щелчки сейфа: сам замок невидим, но звук говорит, верно ли подобрана цифра. В Go-приложении такими «щелчками» становятся HTTP-коды (200 vs 404), длина body в байтах, наличие конкретной строки в HTML, и даже задержка ответа. Каждый из этих сигналов превращает односимвольный «да/нет» вопрос к базе в один бит украденных данных.
Изучим три основных способа, которыми хакеры считывают ответы от базы данных. Все три эксплуатируют одну ошибку — fmt.Sprintf или конкатенацию + внутри SQL вместо плейсхолдеров ? / $1.
Важно понимать масштаб: один blind-запрос восстанавливает ~1 бит информации. Пароль bcrypt длиной 60 символов = 480 бит = 480 запросов через >/< (бинарный поиск). На скорости 50 запросов/сек это 10 секунд. То есть техническая дешевизна атаки сравнима с обычным crawl-ом сайта поисковиком — большинство WAF и rate-limiter'ов её не видят, потому что трафик неотличим от легитимного «пользователь кликает по карточкам товара».
1. Метод: HTTP Статус-коды (Самый частый)
Go-разработчики любят чистоту: если объект не найден — верни 404, если найден — 200. В типичном chi/gin-хендлере это идиоматично: if errors.Is(err, sql.ErrNoRows) { c.Status(404); return }. Но эта же дисциплина превращает каждый запрос в бинарный оракул: статус-код буквально проговаривает результат подзапроса.
?id=1 AND (SELECT 1)=1→200 OK(Да, верно)?id=1 AND (SELECT 1)=2→404 Not Found(Нет, ложь)
Этого достаточно, чтобы хакер "нащупал" логику приложения. Скрипт на Python с requests отправит 100 запросов и по статус-кодам восстановит произвольный байт пароля. Никакого «утечка SQL-ошибки в ответ» — типовой статический анализатор такую уязвимость даже не отметит, потому что текст ошибки не возвращается клиенту. Уязвимость живёт в самой бизнес-логике 200/404.
2. Метод: Текстовые маркеры (Content-based)
Если статус всегда 200, хакер смотрит на текст страницы. Это типично для приложений на html/template, где «не найдено» рендерится как обычный HTML с тем же 200-м кодом — фронтенд-разработчик настоял на «красивых ошибках без редиректов». Атакующий просто сравнивает длину body или ищет якорную подстроку.
| Условие (Payload) | Текст на странице | Что это значит? |
|---|---|---|
AND 1=1 |
"Данные пользователя: admin" | TRUE (Истина) |
AND 1=2 |
"Пользователь не найден" | FALSE (Ложь) |
Хакер просто пишет простой Go-скрипт (или использует sqlmap), который ищет подстроку "не найден" в ответе сервера. На стороне атакующего это две строки: strings.Contains(body, "не найден") → разница есть → бит=1, нет → бит=0. Дальше — цикл по символам пароля.
Типовая ошибка Go-разработчика здесь — попытка «спрятать» вывод через одинаковый шаблон страницы при ошибке: текст «Данные пользователя» убрали, а длина HTML всё равно отличается на десятки байт (другой блок в layout, другое meta-description). Атакующий считает не строки, а Content-Length — и оракул всё равно работает.
Особый случай — Go-приложения с text/template для сообщений об ошибках. Разработчик меняет «Пользователь не найден» на «Запрашиваемые данные недоступны» — кажется, что оракул скрыт. Но sqlmap ловит разницу не по слову, а по сходству Левенштейна между двумя ответами с известными true/false условиями. Достаточно 5 байт разницы — оракул восстановлен.
3. Как хакер "усиливает" инъекцию
Хакер может использовать более сложные условия, проверяя части секретных данных. Каждое условие — SQL-функция, чьё значение склеивается с осью «истина/ложь» через AND:
' AND (SELECT is_admin FROM users WHERE id=1)=TRUE--
Если сервер вернул 200 OK — хакер узнал, что пользователь #1 — это администратор! Хотя он не видел никаких паролей на экране. Точно так же он переходит к посимвольному извлечению: AND ASCII(SUBSTRING((SELECT password FROM users WHERE id=1),1,1)) > 100 — и за 8 запросов через бинарный поиск восстанавливает один символ. Пароль из 32 символов → 256 запросов. Несколько секунд для современного скрипта на httpx.
4. Почему это критично для Go?
Даже самый защищенный (с точки зрения вывода ошибок) Go-сервер на Fiber или Gin всё равно реагирует на базу данных. Если вы не используете параметры (?), хакер по этой реакции прочитает вашу базу.
История подтверждает масштаб: Heartland Payment Systems в 2008 году потеряла 134 миллиона карт через классическую SQLi в платёжном шлюзе. British Airways в 2018-м — 380 тысяч транзакций через инъекцию в стороннем скрипте, штраф GDPR €22M. TalkTalk в 2015-м — 157 тысяч клиентов, £77 миллионов потерь. Во всех случаях атака начиналась именно с blind-разведки по статус-кодам: атакующие даже не видели данных, но восстановили схему и пароли по одному биту за запрос. Это OWASP Top 10 A03:2021 (Injection, третье место по критичности) и CWE-89 — единственный класс уязвимости, который и в 2025-м не уходит из топ-3.
Индикатором истинности может быть что угодно: статус-код, размер страницы в байтах или даже время ответа. Защита одна и не зависит от типа индикатора — параметризованный плейсхолдер ? (или $1 для pgx) превращает любой ввод в литерал, и AND 1=2 уже невозможно подставить в дерево SQL-запроса как условие. База видит литеральную строку, а не команду.
Теперь посмотрим, как хакер превращает эти простые ответы «Да/Нет» в восстановленные по буквам пароли.
Продолжить чтение
Что бы прочитать модуль полностью, зарегистрируйтесь/войдите на платформу
Когда закончишь — отметь раздел, чтобы продолжить.