Pull to refresh

Comments 17

И это заключительная часть цикла статей про SQL-инъекции.

И где ссылки на другие две части?

Соглашусь, что упрек к оформлению статьи справедливый, ссылку желательно ставить прямо в тексте. Но если вам действительно интересно, то это один лишний клик мышкой: ник автора — вкладка "Публикации".

Ошибся ссылкой, написал ниже

Вот и зря, кстати, не читали :)
Потому что я там как раз и пояснял в комментариях, что все эти ухищрения работают только если приложение не защищали от SQL инъекций или делали это неграмотно. И в отличие от миллиона статей про эксплуатацию инъекций, сама защита сводится к одному простому правилу.


Только, разумеется, это не "экранирование (prepare/execute)"). Грамотно принцип защиты от инъекций формулируется так:


SQL запрос на 100% должен состоять из элементов, напрямую прописанных в коде программы, и в него никогда не должны добавляться обрабатываемые программой данные.

Только следование этому правилу гарантирует 100% защиту от инъекций. И "prepare/execute" (без всякого "экранирования") будет работать, только если используется для реализации озвученного выше принципа. А не в виде бездумного карго-культа, который можно встретить сплошь и рядом


$stmt = $pdo->prepare("SELECT something FROM table WHERE username = '$name'");
$stmt->execute();  // позор и инъекция
в него никогда не должны добавляться обрабатываемые программой данные.

Далеко не любая часть запроса может быть параметризована, так что бывают случаи, когда вы просто вынуждены строить запрос из данных чуть более, чем полностью. Эти случаи не такие частые, но они есть.

Совершенно верно. Именно поэтому так и сформулировано, без конкретики. А более подробные рекомендации приведены ниже, где как раз и сказано, что помимо передачи данных через параметры,


  • любые другие элементы запроса (идентификаторы, ключевые слова) попадают в него только из списка вариантов, заранее прописанного в коде (про это правило часто забывают, хотя оно является не менее важным)
списка вариантов, заранее прописанного в коде

Это тоже несколько категорично. У меня вот имя таблицы, список колонок, и т.п. известны только в рантайме. Тем не менее, я уверен, что инъекции в моем приложении невозможны. В конце концов, SQL формальный язык, для него грамматика существует (пусть ее и не всегда просто добыть), и мы всегда можем смоделировать безопасное поведение подстановки параметров в произвольное место запроса (которое мы, как авторы кода, точно знаем). И если я подставляю имя таблицы — я знаю, что это именно имя таблицы, и вполне могу провалидировать его.


P.S. Хотя если рассматривать это как учебный текст, то да, лучше начать с более строгих утверждений. Пусть даже чуть упрощенных.

Совершенно верно. В умелых руках и экранирование работает, если применяется по назначению, а не "для защиты от инъекций".


Но если мы говорим о передаче знаний, то желательно использовать формулировки не допускающие двойных толкований. Ибо сказано, "все, что может быть понятно неправильно, будет понято неправильно". А как только вы начнете формулировать эту валидацию, которая у вас работает, то либо получится объяснение в три этажа, либо получится неуниверсально (я слишком много повидал колонок с именами "итоговый баланс" в существующих базах, чтобы проверять по [0-9a-z_]+), либо что-то упустите. Либо пропустите такой вариант инъекции, когда клиент добавит поле, в передаваемый на сервер джейсон, имя которого пройдет валидацию, но к которому клиент не должен иметь доступа.

Просуммирую свои комментарии к предыдущим частям


  • Во-первых, в подобных статьях надо обязательно разделять понятия собственно уязвимости и её эксплуатации. Потому что по факту каких-то особенных "слепых" инъекций не бывает.
    • Сама уязвимость (SQL инъекция) всегда только одна: это возможность модифицировать код SQL с помощью передаваемых в запрос данных. Если эта возможность есть — значит есть и инъекция. Даже если в статье, которую вы прочитали, нет готового рецепта для ее эксплуатации. А "слепая инъекция" — это не отдельный тип инъекций, как можно подумать из названия, а просто набор техник, эксплуатирующих ту же самую уязвимость. Просто в некотором ограниченном окружении. Частный случай, который на самом деле не стоит выделения в отдельную категорию.
    • А вот способов использовать (эксплуатировать) эту уязвимость — бесконечное множество. Важным следствием понимания этого факта является простое правило — что защищаться следует не от бесконечного количества разных вариантов эксплуатации, а от одной простой уязвимости, с помощью простой рекомендации, которая приведена в комментарии выше.
  • Во-вторых, сам по себе формат подобных статей видится мне довольно бессмысленным. По сути, они являются таким ликбезом по SQL для чайников. В котором банальные элементы SQL выдаются за сокровенные знания. "Оооо, в SQL, оказывается, есть UNION! Оооо, в SQL, оказывается, есть sleep()!".
    Фактически, умение эксплуатировать инъекции сводится к владению SQL. Эксплуатация инъекций — это на 50% знание SQL и на 50% — творчество и умение нестандартно мыслить. А совсем не зазубривание нескольких приёмчиков из статьи. Вот, кстати, совершенно гениальная история взлома: https://habr.com/ru/articles/708384/. Хотя она и не про инъекции, но хорошо показывает сам процесс.
  • В-третьих, как часто правильно пишут в комментариях, подобная статья всегда является неполной, если не предлагает способов защиты от инъекций. Причем из сказанного выше становится понятно, что для защиты знание всех этих ухищрений уже не требуется. И вот её-то как раз и можно свести "к зазубриванию пары приемчиков". Чтобы обеспечить выполнение правила "SQL запрос на 100% должен состоять из элементов, напрямую прописанных в коде программы, и в него никогда не должны добавляться обрабатываемые программой данные" достаточно двух простых действий:
    • любые данные передаются в запрос только через символы подстановки (параметры, плейсхолдеры)
    • любые другие элементы запроса (идентификаторы, ключевые слова) попадают в него только из списка вариантов, заранее прописанного в коде (про это правило часто забывают, хотя оно является не менее важным)

Как можно видеть, в озвученных выше правилах отсутствуют такие мусорные термины, как "экранирование" и "пользовательский ввод". У которых не существует четкого определения, но которые, к сожалению, до сих пор очень часто можно встретить в рекомендациях по защите от инъекций.

Мои методы защиты:

  1. Подготовка запроса (prepare).

  2. Из введенного пользователем id удалять все знаки кроме цифр.

  3. При составном sql проверять список допустимых операций (swith).

Наверно повторяюсь.

Подготовка запроса (prepare).
Из введенного пользователем id удалять все знаки кроме цифр.
При составном sql проверять список допустимых операций (swith).

Я вас умоляю, ну зачем было позориться и писать эту чушь?
Я же писал выше, что само по себе использование prepare() ни от чего не защищает. А защищает использование параметров в запросе.


Какое отношение к защите имеет удаление каких-то знаков? Без этого будет инъекция, серьёзно? Вы здесь путаете валидацию с защитой от инъекций. Валидация не имеет отношения к защите, она относится к бизнес-логике.


"Список допустимых операций (swith)" — в принципе, это бессвязное предложение можно истолковать как использование п. 2 из моих рекомендаций, но во-первых, совершенно неясно, что имеется в виду под "составным sql", а во-вторых, switch для этого никогда не используется. В современных ЯП есть много других способов проверить, входит ли значение в список.

Теперь несколько комментариев по тексту


При слепой SQL-инъекции многие методы, такие как UNION-атаки, неэффективны

Мягко говоря, это довольно странное заявление. Какой-то gatekeeping, "если запрос ничего не выводит, то не используйте UNION". И что мне теперь — не авторизовываться на сайте с хэшированными паролями через примитивную инъекцию с UNION? А что тогда? Подбирать, как рекомендуется в статье, хеш пароля по одной букве?


Это хорошая иллюстрация к сказанному ранее, что эксплуатация инъекций — это не набор трюков, а творческий подход.


где LIMIT 1 это колонка. Если true, колонка пользаков существует

Если кто-то смог понять смысл этих двух предложений, прошу прокомментировать. Общий смысл этих телодвижений понятен, но вот пояснения, похоже, писались под веществами. Что значит "LIMIT 1 это колонка"? Почему колонка "пользаков", если речь идет явно о таблице?
Плюс отсутствующая таблица тупо вызовет ошибку. И надо либо запрашивать из information schema, либо обрабатывать как ошибку, приемами из следующего раздела.


Предшествующая техника не сработает, поскольку введение различных булевых условий никак не повлияет на ответы приложения.

Надо исправить на "Предшествующая техника не сработает, если результат запроса не влияет на ответы приложения", поскольку в текущем виде предложение выглядит бессмысленным.


`xyz' AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='a

пример оторван от реальности. читатель должен сам догадаться, что на место условия 1=2 надо подставить какую-то полезную нагрузку, которая сообщает информацию о базе данных.


То же самое касается и самого последнего примера в статье. "Ну вы там дальше сами". Зачем вообще было его приводить — загадка.


В целом, видно что статью писал человек, который не понимает приводимых в ней примеров, что очень печально.

Для алфавитно-цифрового формата куки сделать фильтрацию (perl):

$kuka =~s/[^A-Za-z0-9]//g;

и всё.

Поймите, кроме одной куки есть еще 20. Все в разных форматах. А кроме кук есть еще 100500 других входных параметров. Вам совсем больше нечего делать, кроме как валидировать каждый в соответствии со своим набором параметров? А что делать с куками у которых не алфавитно-цифровой формат?


Вы можете себе представить программу не из 5 строчек, а примерно из 50 000? В которой на момент записи в БД система понятия не имеет, в какой переменной должны лежать "алфавитно-цифровые" данные, а в какой не должны?


Зачем вообще сюда тащить валидацию входных данных? Она не имеет никакого отношения к защите от инъекций.


Вы в состоянии понять, что для каждой операции должен использоваться свой инструмент?
Для валидации данных в соотвествии с бизнес-логикой — один.
Для защиты от SQL инъекций — совсем другой

Мил-человек, когда Вы полностью пишите свой код у Вас есть возможность фильтровать всё входящее и фильтровать по-разному, в том числе удалять потенциально опасные символы.

Я не использую никакие инструменты, я просто пишу код и получаю то, что мне надо с минимальными затратами ресурсов.

Sign up to leave a comment.

Articles