- PVSM.RU - https://www.pvsm.ru -
Я знаю, что тема SQL инъекций уже всем набила оскомину :)
Однако тема очень волнительная о ней постоянно говорят и раздувают огонь недоверия к себе, нагоняют панику и страшно становится даже тем, кто был уверен в своем коде.
О том, как не допустить инъекций была уже масса статей — повторять не буду — сводится все к нескольким банальнейшим пунктам практики:
Обязательно проверяйте тип и приводите к тому, что Вы ожидаете.
Если Вы ждали целочисленное число, а пришли буквы и(или) точка — сообщаем об ошибке куда-нибудь, а клиенту выдаем 500 / 404 / или нужный Вам код в ответ.
Мой пример ф-ии для unsigned integer:
function getAsUint( &$var ) {
return ( preg_match( "~^\s*(\d+)\s*$~i", $val, $t ) ) ? $t[1] : null;
}
На вход она получает исследуемую переменную, по выходу она возвращает корректное значение, или null, если значение некорректное или пустое.
Это лишь пример, но суть понятна — Вы можете сделать свою супер функцию с блекджеком и феями нетяжелого поведения.
В PHP можно воспользоваться функциями фильтрации тут про них есть [1]
Надо помнить и учитывать возможность пробелов во входных параметрах форм. Например — эта функция корректно отбросит пробелы вернув лишь число.
Таким образом «123» и " 123 " для этой функции — корректные значения, но вернет она всегда «123»
Т.е. надо расставлять правильные кавычки, как для названий полей, таблиц и др, так и для любых значений.
Во-первых, это убережет Вас от ненужных пауз за размышлением: “ставить или нет кавычку”, а во вторых, оставит меньше шанс написать потенциально уязвимый запрос типа
"select * from `table` where `id`={$id}"
( я думаю у многих, когда дело касается id написано именно так ;) )
Можно использовать mysql_real_escape_string, можно свое.
Вот моя функция:
function prepareStr( $str ) {
return "'". str_replace(
array( '\', "", "n", "r", "'", '"', "x1a" ),
array( '\\', '', '\n', '\r', "\'", '\"', '\Z' ),
$str ) . "'";
}
используется например так:
"select * from `table` where `id`=" . prepareStr( $tag )
Кавычки внутри параметров для нее не нужны — она их подставляет сама — это убережет от того, что можно случайно забыть про п2 :)
Данные по замене взяты из мануала mysql отсюда [2]
Теперь немного про набившую ужу оскомину mysql_real_escape_string — один из параметров которой — connection id.
Мне, например, это не очень удобно, ибо не при всех использованиях у меня он есть
Но ведь он зачем-то нужен этой функции? Писали что без нее она работает некорректно :)
Если заглянуть в исходники mysql, то эта ф-я лежит в mysys/charset.c и использует идентификатор чтобы по нему получить CHARSET_INFO, а его в свою очередь чтобы определить лишь мультибайтная кодировка или нет.
И сколько длина одного символа. Во всяком случае, я это так понял из листинга. Быть может тут есть гуру C? Поправьте меня если я не прав.
Поскольку я работаю с UTF при коннекте с базой или же в самом худшем случае с WIN1251, то можно вполне обходиться prepareStr не заморачиваясь с кодировкой, вот если она у Вас действительно экзотическая — тогда лучше использовать mysql_real_escape_string.
У функции prepareStr как и у mysql_real_escape_string есть одна уязвимость — дело в том, что они обе никак не экранируют "%" и "_" которые используются в конструкции типа LIKE.
Внутри LIKE работает экранирование вида "%" и "_" а вот вне — нет — вернет два символа. Почему? — неизвестно, но это так.
По этому если Вы хотите избавится от этой неоднозначности я вижу два пути — либо использовать в подстановках для LIKE другой вариант функции, либо же использовать одну функцию, но для универсальности придется заменять все символы “% “и “_” на нечто вида
"CONCAT( 'строкаДо%или_', '%или_', 'строкаПосле%или_ ')"
Возможно есть и лучшее решение, но я о нем не знаю.
Это, кстати, абсолютно разные вещи.
Кратко так — placeholders — это помощник, формирующий обычный текстовый запрос к БД, но эскейпящий все параметры сам, т.е. он избавляет от необходимости "...". mysql_real_escape_string( $param). "...".
А prepared statements — это способ попросить подготовить запрос базой данных, например для многократного вызова.
С моей точки зрения использовать для обычного сайта и обычного CRUD prepared statement — это как по воробъям из базуки стрелять. У них есть много подводных камней с кэшированием и т.д.
Если Вы их знаете или знаете что это Вам необходимо – ну тогда дело уже другое.
При написании запросов нужно использовать
А если без юмора — мне смешно, когда люди, использующие PDO или prepared statement, считают что все, SQL инъекции в их проектах невозможны — это не так, ибо все это — лишь ИНСТРУМЕНТЫ, снижающие количество мест, где нужно подумать, но не убирающие их.
И если человек сделал уязвимый запрос, то все равно, через что он его закинет в базу — он будет уязвимым.
Пример, встретившийся в одном из проектов, что я разбирал (типа такого):
"select * from `{$table}` where `col` LIKE ?"
И система все правильно эскейпила и подставляла, только вот в переменной $table наш кодер был очень уверен — ибо бралась она из файла конфигурации плагина для движка.
А лежал этот файл в директории, куда был разрешен upload…
И вот так, из-за одного уязвимого плагина слили плохие дяди всю базу…
Ну вот как то так.
Надеюсь, я кому-то помог c кашей в голове после кучи статей и чтений ну как же защитится, и уверенных в своем коде людей стало больше.
Жду Ваших комментариев и дополнений и спасибо Вам что дочитали до конца :)
P.S. Про ошибки, если найдете – пожалуйста в личку.
Автор: ionicman
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/mysql/12316
Ссылки в тексте:
[1] тут про них есть: http://php.net/manual/ru/book.filter.php
[2] отсюда: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
[3] МОЗГ: http://www.braintools.ru
Нажмите здесь для печати.