Pull to refresh

Comments 60

От всего перечисленного спасёт банальнейший PDO. Он сам всё проверит и экранирует как надо.


А от XSS спасёт банальнейшее принудительное экранирование всех выводимых переменных, которое есть в любом нормальном шаблонизаторе. Ну и jevix и аналоги, если html всё-таки нужен.

Prepared statements / parameterized queries are generally sufficient to prevent 1st order injection on that statement*. If you use un-checked dynamic sql anywhere else in your application you are still vulnerable to 2nd order injection.
2nd order injection means data has been cycled through the database once before being included in a query, and is much harder to pull off. AFAIK, you almost never see real 2nd order attacks, as it is usually easier for attackers to social-engineer their way in.
You can accomplish a 2nd order injection attack when you can cause a value to be stored in a database that is later used as a literal in a query. As an example, let's say you enter the following information as your new username when creating an account on a web site (assuming MySQL DB for this question):
' + (SELECT UserName + '_' + Password FROM Users LIMIT 1) + '

А теперь по-русски и с полным примером вместо непонятного огрызка, пожалуйста

Так и думал, что меня заминусуют, но я так ничего и не понял. Кто будет в здравом уме писать такой запрос, который приведён в этом огрызке? Злоумышленник его написать не сможет, потому что PDO всё заэкранирует, а программисту такое писать зачем?

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


К счастью, тех пор тот ответ подправили, и он звучит теперь нормально, хотя и в стиле капитана Очевидность:


Да, подготовленные запросы защищают от инъекций, но только если используются для всех запросов в приложении. Если же это не так, то ждите инъекции второго порядка.
UFO just landed and posted this here
Собсна ничего не мешает проверять входные данные в регулярке вида ^(тут_условие)$, или использовать менее параноинальный вариант, но использовать группу.
Или прописать пару правил для args и location в nginx, или просто собрать последный с naxsi и снова поколдовать.
Ничего не мешает, но A1 — SQL injection возглавляет список OWASP который год подряд.
SQL инъекции как класс лечатся связыванием переменных.
Плюсом идет ускорение работы с БД.

https://www.google.ru/search?q=sql+bind+variable

Если уж про PHP, то:
http://php.net/manual/ru/mysqli.prepare.php
http://php.net/manual/ru/function.pg-prepare.php
http://php.net/manual/ru/function.oci-parse.php
и т.д. для остальных баз.

Ускорение работы с БД получается только при отправке нескольких запросов с одной структурой, но разными параметрами.


Если же запросы все разные, то работа с БД замедляется: вместо одного запроса к БД нужно сделать два (prepare + execute).

Суть prepared statement в том, что приложение еще на этапе инициализации приложения перекачивает все свои шаблоны sql-запросов на сервер базы данных. Если какая-то часть запросов формируется динамически, и они кажутся на первый взгляд разными, то все равно их можно повторно использовать, следовательно к ним уже не надо применять повторные prepare. т.е. по-любому будет ускорение. Надо просто сохранить все дескрипторы этих «разных» sql запросов для повторного использования просто поместив их в общий пул.

Суть в том, что некоторые языки созданы, чтобы умирать. И даже хотя у этих языков есть режимы, в которых они не умирают — основной режим все же подразумевает смерть после каждого запроса. И потому у пишущих на таких языках нет никакого общего пула для запросов, который можно было бы подготовить на этапе инициализации приложения. Даже пула соединений — и того нет.


Вот и пытаются экономить на числе prepare-запросов...

То что процесс обработавший запрос должен умиреть — это просто исторически сложившаяся практика. Умирают процессы именно тех или иных движков, CMS, framework. А процессы на других движках этих же языков программирования могут жить годами и ничего с ними не происходит. Просто многие движки родились до появляения технологии prepared statement. И просто так их теперь не переделать. Это совершенно иная концепция. Но если с нуля писать проект можно практически на любом языке полноценно использовать весь стек технологии prepared statement. Было бы желание и умение. И не важно это java, php, perl или еще что-то.

Врочем, если какой-то язык программирования технически не поддерживает prepared statement, то вероятно это именно случай когда умирает сам язык программирования, а не его процессы.

Вы так пишите, как будто prepared statement является единственным, что отделяет "умирающие" варианты php от "неумирающих". На самом же деле это — последнее в списке различий.

На php все эти prepared запросы умирают. Они будут жить, только если использовать демоны вида phpDaemon, reactphp, prefork.
mysql их может кешировать и польза всё равно будет
Не может. Нет там никакой пользы, только замедление и лишние накладные расходы в парадигме die always. Query Cache это другое, он работает независимо от того используются ли Prepared statements или нет.
В пределах сесии может
The server maintains caches for prepared statements and stored programs on a per-session basis. Statements cached for one session are not accessible to other sessions. When a session ends, the server discards any statements cached for it.


То есть от кучи запросов от ORM вида:
SELECT * FROM sometable WHERE id = ?


Будет повторное использование и может быть даже польза =)
В целом согласен с вашим утверждением.
За такое — select в цикле — по рукам надо бить в продакшене, максимум можно в каких-нибудь небольших вспомогательных утилитах делать. Запросы такого вида должны выглядеть либо как Ic IN (?,?,?), либо как JOIN, либо как subselect в новых версиях mysql, где они не уступают по скорости JOIN.
Тщательно проверять входящие данные.
Использовать плейсхолдеры, про которые в статье почему-то не написано.
А можно тупой вопрос к знатокам? :)

Если собираются данные в SQL вопрос типа такого:
'SELECT * from `table` where `id`="' + Экранирование( param ) + '"'

Где Экранирование заменяет
\, \0, \n, \r, ', ", \x1a
на соответствующее со слэшем
\\, \\0, \\n, \\r, \\', \\", \\Z

Возможна ли инъекция?
И если да — то каким образом?

Заранее благодарю за ответ.

Ну зачем, зачем так делать в двадцать первом веке? Чем вам параметризованные запросы не угодили?

Что такое параметризованные запросы я в курсе.

А не затруднит все-таки ответить на вопрос?
Мне интересны именно способы атаки на такой вектор защиты — вот и все, ну и, судя по ответам, атаку не возможно провести :)

Для начала, уточните с какой СУБД работаете.

Для MariaDB/MySQL ваша функция эквивалентна mysql_real_escape_string, о чем вам уже написали ниже пока я забывал этот сделать :), поэтому напрямую SQL-инъекцию тут не засунуть.


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

И вот так каждый раз — я этот вопрос задавал много-много раз — и так никто мне ответить на него и не смог :)
А ваш вопрос некорректен на самом деле. Опишите функцию «Экранирование (param)». В вашем вопросе это некая абстракция, которую сложно оценить. Или приведите ее исходник. А то (если честно), даже не хочется время терять на гадание.
Допустим, по вашей постановке задачи я решу, что ваша функция «Экранирование», это что-то типа
preg_replace("/'/", "\\'", $param);

а $param — это $_GET['id'].

Подходит мое предположение под описание в вашей задаче? Подходит.
Так это на самом деле? Не уверен.
Вот и не отвечает вам никто. В вопросе конкретики не хватает, а ванговать не все умеют.
даже не хочется время терять на гадание

Странно, а зачем гадать, если можно просто уточнить? Ведь это совсем не сложно и при этом очень часто бывает так, что то, что одному очевидно, другому совсем непонятно.
И это вполне нормально :)

Без проблем, если на PHP, то пусть будет так (regexp использовать избыточно):
function screen( $s ) {
	return str_replace(
		array( '\\', "\0", "\n", "\r", "'", '"', "\x1a" ),
		array( '\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z' ),
		$s
	) ;
}

Естественно, считаем что кодировки все настроены правильно.
Аргумент — пусть будет что-то из $_REQUEST / $_GET / $_POST.

Ну и заранее благодарю за ответ и за время, потраченное на мой коммент.
Ok, спасибо, теперь понятно. Навскиду с учетом того, что в sql запросе подставляемое значение вы обернули в одинарные кавычки мне кажется что sql-иньекция в данном случае проблематична.
Спасибо за ответ.

На самом деле первоначально в PHP плейсходеры делались внутри драйвера именно через функцию, и как правильно здесь заметили — это исходник mysql_real_escape_string.

И мне тоже не видится абсолютно никакого способа сделать здесь инъекцию — но всегда было интересно — может быть это просто я не вижу :)
Нужно еще на детали смотреть.

Например, есть sql_mode=NO_BACKSLASH_ESCAPES, есть хитрые восточные кодировки, в которых нельзя просто все экранировать бэкслэшами.

Это что я сходу могу вспомнить, может и еще что-то есть…

Поглядел исходники mysql_real_escape_string — она ровно тем же самым и занимается)

Не надо ничего "проверять". Надо правильно работать с базой данных: использовать prepared statements, а где это не подходит — правильно форматировать SQL-запрос с учетом синтаксиса SQL.


Если бы хабр "проверял", вы бы не смогли запостить эту статью.

Мне кажется — из нативных тут в первую очередь плейсхолдеры, приведение типов, здравый смысл грамотный алгоритм обработки запросов. При чем тут хтмл, (в статье то про sql инъекци) слегка неясно. Да и хтмл — он и в Африке хтмл, тот кто парсит его регекспами такие статьи не читает.

После прочтения мне даже стало интересно посмотреть на эти «системы безопасности», которые подобным образом обходятся с входными данными. Это же натуральное вредительство — кто ими пользуется?

В основном в банках, процессингах и так далее. Там такой агрессивный черный список не помешает (с реальными данными не пересечется), а от уязвимости в очередном апдейте софта от вендора, который решил сэкономить и нанял индусов на апворке, может спасти.


Вроде бы web application firewall даже обязателен по PCI-DSS, но тут не эксперт, могу и ошибаться.

Защита от SQL-инъекций очень простая — используйте запросы с параметрами! Не составляйте запросы путём сложения строк! А ещё лучше используйте ORM.

Пришла идея в голову
Использовать белый список при работе с символьной обработкой
И черный список слов, состоящих из белых символов
Т.е., допустим, можно передавать только буквы латинского алфавита. И в черный список добавить все зарегистрированные слова(SELECT и т.д.)

Во-первых, у такой защиты будет очень ограниченная область применения, так как она даже ваш же комментарий не пропустит. Во-вторых, все слова в чёрный список не подобавляешь, а даже если и подобавляешь, то в какой-нибудь новой версии MySQL появятся ещё слова, которые все забудут добавить. В-третьих, ну блин есть экранирование, PDO, prepared statements, нечего велосипеды городить

PDO да, согласен, но такая защита нужна не только для баз.
P.S: Если экранировать все, что не в белом списке? Так и данные передаются и запросы не будут выполнены

Зачем всё то что не в белом списке, достаточно просто экранировать вообще всё) Неважно, для баз или чего-то ещё

Всё содержимое статьи следует заменить тремя буквами: PDO.
Заголовок можно оставить.
В каком году PDO появился, 2004-2005? Вот настолько лет рассуждения автора отстали от жизни.
Ладно, пусть не PHP, хотя автор выбрал для примеров его, пусть другой язык. Но и там слой абстракции работы с бд будет уметь биндинг данных, чтобы не городить этих велосипедов.

Такая некомпетентность в корп. блоге. Хабр… Рука-лицо…
Если вы используете PDO — это прекрасно. Обратите внимание — статья не о методах защиты, а о методах ее обхода. Существует защита от инъекций — это и PDO и другие методы, указанные в комментариях. Можно вообще не использовать БД — вообще самый радикальный метод =) К сожалению не все придерживаются такого мнения, поэтому SQL-инъекции все еще так распространены/

Вот только PDO обойти невозможно.

Не PDO, а систему передачи параметров в запрос через плейсхолдеры. А обёртка вокруг плейсхолдеров может быть любая.

Всё ещё жду способов обхода PDO.)

Нельзя строго сказать, что это способ обхода PDO, но в экзотическом случае с не вполне корректной конфигурацией...


http://stackoverflow.com/a/12202218

Неважно о чем сама статья. В конце статьи есть заключение. В нем написано «Тщательно проверять входящие данные». Совершенно дикий совет. Будь там написано «используйте параметризированный SQL» — статья была бы хорошей. Правда, очень короткой, и ссылку на WAF в нее не получичилось бы вставить :)

А так — это не современная статья по защите от инъекций, а просто набор хитрых способов обхода «защитных костылей».
Статья так и называется: методы обхода защитных средств веб-приложений при эксплуатации SQL-инъекций.
Ну так и я о том же. Стандартная защита от SQL-инъекций — параметризированные запросы (через PDO в случае с PHP). В статье об обходе параметризации — ни слова. Все остальное — это не защитные средства, а «защитные средства». А в названии статьи у вас кавычек почему-то нет.
Почти «поваренная книга хакера» вышла, ведь, к сожалению, нынешние молодые хакеры зачастую ищут не «как взломать конкретный сайт/базу», а ищут сайт/базу которые им удастся взломать подсмотренным где-либо способом.
Ну и способ сразу заносится в копилку.
Равно как и сайт.
При переборе запросов неизбежны ошибки mySQL, если при возникновении ошибки командой mail() слать админу оповещение с текстом, это предупредит что сайт под атакой. Да и вообще интересно посмотреть будет сам запрос.
Периодически курирую всякие проекты от мелких до сперкрупных на тему: «Чтобы в базе данных все летало и чтобы нас не сломали sql-инъекциями». 90% проектов, которые я курировал уже после их разработки использовали все что только можно придумать кроме использования банальных prepared statement. Или вообще никак не фильтровали входящие данные и даже не пользовались функцией экранирования. Как всегда на фразу: парни переходим на использование prepared statement начинаются споры разрабов: «Никто так не делает. Первый раз про это слышу. У меня в багаже туева хуча проектов но никто не требовал использовать prepared statement.»

А уж услышав что есть возможность еще на этапе инициализации приложения перекачать все sql-запросы на сервер базы данных, проверить их там на ошибки и т.п., вообще у многих срывает крышу, так как для многих это вообще не вписывается в их картину мира.

Но самое интересное становится когда приложение, наконец, научится работать с prepared statement. Во время запуска начинает иной раз десятки секунд (если запросов десятки тысяч) перекачивать свои sql-запросы на сервер базы данных. И ты ошалевшим разрабам объясняешь, что этим мы перенесли значительную нагрузку с онлайн-режима работы приложения в офлайн. Из кода приложения можно убрать кучу обработчиков исключений по синтаксическим ошибкам в SQLзапросам. Что тоже снижает нагрузку.

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

Но самое приятное получать зарплату 3-6 месяцев просто объясняя разрабам что такое prepared statement и рассказывая о всех плюшках.

Десять секунд запуск на сервере? Отлично, вы замечательно замедлили процесс отладки! Впрочем, до чудес шарика (10 минут на запуск) вам еще далеко, надо больше стараться.

Кстати, а как ваши подготовленные запросы дружат с пулом соединений к серверу?

А можете пояснить как используется строка
/? Id = 1 + union + select + 1,2,3 / *

Насколько я понял — это строка является целевой для исполнения. Но исполнения кем/чем?
Просто я не понимаю целевого значения /? и /* в конце строки… как это работает?

/? — это часть URL. Полный будет выглядеть как-то так: http://example.com/?Id=1+union+select+1,2,3/*


/* — это начало комментария. Используется чтобы выкинуть весь последующий код в запросе.


    • это URL-закодированный пробел.

Sign up to leave a comment.