Я знаю, что тема SQL инъекций уже всем набила оскомину :)
Однако тема очень волнительная о ней постоянно говорят и раздувают огонь недоверия к себе, нагоняют панику и страшно становится даже тем, кто был уверен в своем коде.
О том, как не допустить инъекций была уже масса статей — повторять не буду — сводится все к нескольким банальнейшим пунктам практики:
Все данные из любых внешних источников (даже если эти данные берутся из XML, забираемого из супер секретного и защищенного сайта) должны проходить проверку.
Обязательно проверяйте тип и приводите к тому, что Вы ожидаете.
Если Вы ждали целочисленное число, а пришли буквы и(или) точка — сообщаем об ошибке куда-нибудь, а клиенту выдаем 500 / 404 / или нужный Вам код в ответ.
На вход она получает исследуемую переменную, по выходу она возвращает корректное значение, или null, если значение некорректное или пустое.
Это лишь пример, но суть понятна — Вы можете сделать свою супер функцию с блекджеком и феями нетяжелого поведения.
В PHP можно воспользоваться функциями фильтрации тут про них есть
Надо помнить и учитывать возможность пробелов во входных параметрах форм. Например — эта функция корректно отбросит пробелы вернув лишь число.
Таким образом «123» и " 123 " для этой функции — корректные значения, но вернет она всегда «123»
В SQL запросе, который вы пишите квотироваться должно ВСЕ, даже то, что вроде как и не нужно — например числовые значения.
Т.е. надо расставлять правильные кавычки, как для названий полей, таблиц и др, так и для любых значений.
Во-первых, это убережет Вас от ненужных пауз за размышлением: “ставить или нет кавычку”, а во вторых, оставит меньше шанс написать потенциально уязвимый запрос типа
"select * from `table` where `id`={$id}"
( я думаю у многих, когда дело касается id написано именно так ;) )
Все данные (а это не только то, что пришло извне но и то, что сформировалось внутри функции движка) должны проходить экранирование.
Можно использовать mysql_real_escape_string, можно свое.
Теперь немного про набившую ужу оскомину 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 другой вариант функции, либо же использовать одну функцию, но для универсальности придется заменять все символы “% “и “_” на нечто вида
Возможно есть и лучшее решение, но я о нем не знаю.
Использовать placeholders / prepared statements (или интегрирующие их PDO / ActiveRecord / ORM и т.д.)
Это, кстати, абсолютно разные вещи.
Кратко так — placeholders — это помощник, формирующий обычный текстовый запрос к БД, но эскейпящий все параметры сам, т.е. он избавляет от необходимости "...". mysql_real_escape_string( $param). "...".
А prepared statements — это способ попросить подготовить запрос базой данных, например для многократного вызова.
С моей точки зрения использовать для обычного сайта и обычного CRUD prepared statement — это как по воробъям из базуки стрелять. У них есть много подводных камней с кэшированием и т.д.
Если Вы их знаете или знаете что это Вам необходимо – ну тогда дело уже другое.
Ну и самое главное, что надо усвоить про SQL инъекции — серебрянной пули нет.
При написании запросов нужно использовать МОЗГ и ЖОПУ. МОЗГ — потому что он должен понимать, что он пишет, а ЖОПУ – потому, что она чует что что-то не так ;)
А если без юмора — мне смешно, когда люди, использующие PDO или prepared statement, считают что все, SQL инъекции в их проектах невозможны — это не так, ибо все это — лишь ИНСТРУМЕНТЫ, снижающие количество мест, где нужно подумать, но не убирающие их.
И если человек сделал уязвимый запрос, то все равно, через что он его закинет в базу — он будет уязвимым.
Пример, встретившийся в одном из проектов, что я разбирал (типа такого):
"select * from `{$table}` where `col` LIKE ?"
И система все правильно эскейпила и подставляла, только вот в переменной $table наш кодер был очень уверен — ибо бралась она из файла конфигурации плагина для движка.
А лежал этот файл в директории, куда был разрешен upload…
И вот так, из-за одного уязвимого плагина слили плохие дяди всю базу…
Ну вот как то так.
Надеюсь, я кому-то помог c кашей в голове после кучи статей и чтений ну как же защитится, и уверенных в своем коде людей стало больше.
Жду Ваших комментариев и дополнений и спасибо Вам что дочитали до конца :)
P.S. Про ошибки, если найдете – пожалуйста в личку.