Автор вопроса
вот что он пишет
#
Почему на Impossible SQL-инъекция невозможна: механика изнутри
Дело в порядке событий внутри MySQL. Запрос проходит две стадии:
1. РАЗБОР (parse): MySQL читает текст запроса и понимает его структуру: "это SELECT, тут таблица users, тут условие сравнения"
2. ИСПОЛНЕНИЕ (execute): MySQL выполняет готовый план, подставляя значения
Инъекция возможна только если твои данные попадают в запрос ДО стадии разбора. Prepared statements делают так, чтобы они попали ПОСЛЕ. Вот и вся магия.
---
##
Как ломается на Low (данные приходят ДО разбора)
MySQL получает один текст и разбирает его целиком:
Парсер видит
##
Как защищено на Impossible (данные приходят ПОСЛЕ разбора)
Шаг 1: MySQL разбирает запрос без твоего ввода вообще. Структура застывает навсегда: один SELECT, одна таблица, одно сравнение. Больше никаких UNION там появиться не может — план уже составлен.
Шаг 2: твой ввод передаётся отдельным каналом как значение. Сервер кладёт его в готовый план как литерал — как строку для сравнения, а не как текст команды. Парсер к этому моменту уже отработал и твои слова кодом стать физически не успевают.
##
Что видит MySQL на Impossible, когда ты вводишь свой payload
MySQL честно спрашивает базу: "есть ли пользователь, у которого user_id равен строке
---
##
Сравнение одной таблицей
| | Low (склейка) | Impossible (prepared) |
|---|---|---|
| Когда ввод попадает в запрос | ДО разбора | ПОСЛЕ разбора |
| Кем видит MySQL твой текст | ключевые слова команды | строка-литерал |
| Может ли ввод изменить структуру | да | нет, структура заморожена |
| Результат ввода
##
Почему фильтры проигрывают, а prepared statements — нет
Фильтр (Medium/High) — это угадывание: "запретим кавычки, UNION, пробелы...". Атакующий придумывает обход (обфускация, кодировки) — игра в кошки-мышки без конца.
Prepared statements ничего не угадывают. Они убирают саму возможность данных быть истолкованными как код. Не "плохие слова запрещены", а "любые слова — это просто слова". Поэтому это не костыль, а архитектурное решение.
##
Где эта защита всё равно кончается (ты это уже нашёл сам)
1. Другие запросы того же кода: если где-то рядом разработчик склеил строкой
2. Логика: oracle "нашёл/не нашёл" (твой тест
3. Всё остальное приложение: XSS, CSRF, загрузка файлов — prepared statements их не касаются.
---
Коротко одной фразой: инъекция = твои данные разобрались как код; prepared statements = твои данные приходят после разбора и навсегда остаются данными.
Хочешь — теперь добьём Blind SQLi (там как раз тренируем обратное: вытаскивать данные, когда вывод закрыт), или оформим твой первый write-up по DVWA для Codeby?
ЭТО ПРАВДА ИЛИ НЕТ
#
Дело в порядке событий внутри MySQL. Запрос проходит две стадии:
1. РАЗБОР (parse): MySQL читает текст запроса и понимает его структуру: "это SELECT, тут таблица users, тут условие сравнения"
2. ИСПОЛНЕНИЕ (execute): MySQL выполняет готовый план, подставляя значения
Инъекция возможна только если твои данные попадают в запрос ДО стадии разбора. Prepared statements делают так, чтобы они попали ПОСЛЕ. Вот и вся магия.
---
##
PHP:
$query = "SELECT ... WHERE user_id = '" . $id . "'"; // склейка СТРОК
mysqli_query($query); // MySQL разбирает результат
MySQL получает один текст и разбирает его целиком:
SQL:
SELECT ... WHERE user_id = '1' UNION SELECT user, password FROM users#'
└── твои слова стали КЛЮЧЕВЫМИ СЛОВАМИ команды ──┘
UNION, SELECT как приказы. Твой текст участвовал в разборе → он стал кодом.##
PHP:
$stmt = $pdo->prepare("SELECT ... WHERE user_id = :id"); // ШАГ 1: MySQL разбирает ЭТОТ текст
$stmt->execute([':id' => $id]); // ШАГ 2: значение едет отдельным конвертом
Шаг 1: MySQL разбирает запрос без твоего ввода вообще. Структура застывает навсегда: один SELECT, одна таблица, одно сравнение. Больше никаких UNION там появиться не может — план уже составлен.
Шаг 2: твой ввод передаётся отдельным каналом как значение. Сервер кладёт его в готовый план как литерал — как строку для сравнения, а не как текст команды. Парсер к этому моменту уже отработал и твои слова кодом стать физически не успевают.
##
Код:
Запрос (заморожен): SELECT first_name, last_name FROM users WHERE user_id = ?
Значение (конверт): "1' UNION SELECT user, password FROM users#"
MySQL честно спрашивает базу: "есть ли пользователь, у которого user_id равен строке
1' UNION SELECT user, password FROM users#?" Такой строки в базе нет → пустой ответ. Твой payload стал бессмысленным набором символов для сравнения, как если бы ты искал человека по фамилии "QWERTY".---
##
| | Low (склейка) | Impossible (prepared) |
|---|---|---|
| Когда ввод попадает в запрос | ДО разбора | ПОСЛЕ разбора |
| Кем видит MySQL твой текст | ключевые слова команды | строка-литерал |
| Может ли ввод изменить структуру | да | нет, структура заморожена |
| Результат ввода
' UNION ... | выполнение твоего SELECT | сравнение с несуществующей строкой → пусто |##
Фильтр (Medium/High) — это угадывание: "запретим кавычки, UNION, пробелы...". Атакующий придумывает обход (обфускация, кодировки) — игра в кошки-мышки без конца.
Prepared statements ничего не угадывают. Они убирают саму возможность данных быть истолкованными как код. Не "плохие слова запрещены", а "любые слова — это просто слова". Поэтому это не костыль, а архитектурное решение.
##
1. Другие запросы того же кода: если где-то рядом разработчик склеил строкой
ORDER BY $col или имя таблицы — инъекция живёт там. Параметризовать можно только значения, не имена таблиц/колонок.2. Логика: oracle "нашёл/не нашёл" (твой тест
1 vs 999) течёт независимо от синтаксиса.3. Всё остальное приложение: XSS, CSRF, загрузка файлов — prepared statements их не касаются.
---
Коротко одной фразой: инъекция = твои данные разобрались как код; prepared statements = твои данные приходят после разбора и навсегда остаются данными.
Хочешь — теперь добьём Blind SQLi (там как раз тренируем обратное: вытаскивать данные, когда вывод закрыт), или оформим твой первый write-up по DVWA для Codeby?
ЭТО ПРАВДА ИЛИ НЕТ