Comments 95
Использовать нормальный ORM и не знать гембеля… ^_^
+16
Использовать placeholders.
+12
UFO just landed and posted this here
использовать шустрый мозг
0
https://addons.mozilla.org/ru/firefox/addon/7597
«Работает с Firefox: 2.0.0.8 – 3.6a1pre»
в ФФ 3.6 не ставится :(
на сайте разработчиков ссыка на эту же страницу
«Работает с Firefox: 2.0.0.8 – 3.6a1pre»
в ФФ 3.6 не ставится :(
на сайте разработчиков ссыка на эту же страницу
0
Поправил install.rdf
Держите: http://dl.dropbox.com/u/5388799/sql_inject_me-0.4.4-fx.xpi
Держите: http://dl.dropbox.com/u/5388799/sql_inject_me-0.4.4-fx.xpi
+5
Плагин нашёл на главной странице Гугла 16 ошибок
+4
Magic Quotes в рекомендованных способах защиты? Мда
Главное и пожалуй единственное правило — не верить ничему, что приходит от пользователя. Это должно быть не вторым пунктом «Проверка введенных данных в запросах», а первым!
Как вы это будете делать — вопрос второй. Лучше всего подготовленные запросы использовать.
Главное и пожалуй единственное правило — не верить ничему, что приходит от пользователя. Это должно быть не вторым пунктом «Проверка введенных данных в запросах», а первым!
Как вы это будете делать — вопрос второй. Лучше всего подготовленные запросы использовать.
+5
Проверка ввода данных менее эффективна, чем параметризованные запросы и хранимые процедуры
Проясните — как хранимые процедуры могут противостоять инъекции?
0
В параметрах процедуры присутствует строгая типизация, т.е. вы явно указываете тип данных этого параметра, т.е. в IN pId BIGINT окромя числа ничего не вставишь, отсюда отсутствует возможность подбора инъекции
+2
То есть что-то вроде этого не сработает?
1); DROP TABLE Users; /*
1); DROP TABLE Users; /*
+1
Нет, база выдаст сообщение о несоответствии типов, даже более того, если тип параметра допустим IN comment VARCHAR(255), то все равно это не сработает… он будет работать с «DROP TABLE Users; /* » как со строкой, не обращая внимания на инъекцию
+1
Не подумайте, что я придираюсь. Хочется просто разобраться.
Допустим, есть такой запрос в базу:
$var — переменная, которую вводит пользователь на форме.
Введя указанную выше строку, мы получим
Мне кажется, что drop table будет выполнен. Или я не прав?
Допустим, есть такой запрос в базу:
"SELECT * FROM FUNC1( $var );"
$var — переменная, которую вводит пользователь на форме.
Введя указанную выше строку, мы получим
"SELECT * FROM FUNC1( 1 ); DROP TABLE Users; /* );"
Мне кажется, что drop table будет выполнен. Или я не прав?
+1
Вы привели как раз пример динамического запроса.
После прочтения статьи, нашу функцию FUNC1 мы будем вызывать примерно так (смотря как и где будем делать bind): SELECT FUNC1(?pVar) FROM DUAL;
А данные к параметру pVar (тип TEXT) будем привязывать (bind) уже отдельно (как именно — тут от конкретного языка зависит, например для PHP можем использовать prepare), в переменную pVar запишем «DROP TABLE Users; /*» и мы ничем не рискуем.
После прочтения статьи, нашу функцию FUNC1 мы будем вызывать примерно так (смотря как и где будем делать bind): SELECT FUNC1(?pVar) FROM DUAL;
А данные к параметру pVar (тип TEXT) будем привязывать (bind) уже отдельно (как именно — тут от конкретного языка зависит, например для PHP можем использовать prepare), в переменную pVar запишем «DROP TABLE Users; /*» и мы ничем не рискуем.
+2
спасибо.
+1
если мы используем бинды, то при чём тут хранимые процедуры?
0
Не все языки имеют строгую типизацию, чтобы максимально избежать ввода некорректных данных, лучше использовать дополнительную защиту со стороны базы данных, это во-первых, во-вторых логику, раскрывающую подробности вашей базы и структуры хранения, лучше спрятать в процедуре\функции, например, зачем светить чем вы делаете подсаливание паролей в php скрипте, или для увеличения безопасности особо «опасные» запросы нужно разрешить запускать только от root`ого пользователя, такое безопаснее всего сделать через базу. Плюс процедуры вас отучат от динамических запросов в принципе (если вы не будите делать такие же prepare в процедуре разумеется), согласитесь, большое искушение в php не писать два запроса с небольшим отличием, а использовать один с динамической подстановкой данных, которые могут быть опасны. Ну и плюс процедуры создают некий уровень абстракции. Намного проще и быстрее понять и правит что делает код «CALL listAllUser(typeFilter INT)», чем «SELECT id, name, lastName, middleName FROM Users user INNER JOIN userStatus status ON user.statusId = status.id WHERE status.value = ». $status. " and deleted = 0"
0
да-да, и будут у нас тысячи процедур вида listUsersByBirthDayOverAndSexEqualOrderByAgeWithLimitAndOffset x)
0
А как такой вариант #1:
listAllUser(filterId INT) которая внутри фильтрует данные путем join`а к таблице фильтра,
таблица filter имеет тип Memory
заполняем перед вызовом процедуры listAllUser наш фильтр процедурой AddFilter(typeId int, valueId int)
… как то вот так.
Второй вариант:
Вытаскиваем какие-то данные в контроллер (класс работающий со списком User`ов) и в нем реализуем эти самые фильтры (не на стороне базы).
Все зависит от поставленных задач, если мы в динамике должны постоянно менять набор фильтров, то (опять таки в зависимости от количества данных, ширины канала, объема оперативки) выбираем приемлемый вариант реализации: либо в базе это делать, либо на клиенте. Ну и также зависит от конкретной реализации схемы базы.
В общем это тема отдельной статьи)
listAllUser(filterId INT) которая внутри фильтрует данные путем join`а к таблице фильтра,
таблица filter имеет тип Memory
заполняем перед вызовом процедуры listAllUser наш фильтр процедурой AddFilter(typeId int, valueId int)
… как то вот так.
Второй вариант:
Вытаскиваем какие-то данные в контроллер (класс работающий со списком User`ов) и в нем реализуем эти самые фильтры (не на стороне базы).
Все зависит от поставленных задач, если мы в динамике должны постоянно менять набор фильтров, то (опять таки в зависимости от количества данных, ширины канала, объема оперативки) выбираем приемлемый вариант реализации: либо в базе это делать, либо на клиенте. Ну и также зависит от конкретной реализации схемы базы.
В общем это тема отдельной статьи)
0
мсье извращенец? х)))
-1
Мсье проектировал, реализовывал (один и в команде) сложные ERP, CRM системы, некоторые из которых (по лени и не соблюдению вышеописанных правил на некоторых участках кода) подвергались SQL-инъекциям) И код этих участков рефакторился, из-за порчи данных в важных таблицах (например кто-то умудрился испортить хеши всех паролей у пользователей и чуть не сорвал рабочий день огромной компании в трех странах, благо дальше этого ничего сделать не смог\не захотел и мы быстренько восстановили из бэкапа и задумались над проблемой безопасности всерьез)
0
Мсье действительно извращенец.
В данном случае хранимые процедуры БД не дают никакой прибавки к защищенности кода.
Поясню. Перед тем, как создвать динамический запрос нам так или иначе необходимо контролировать тип динамических данных. Иными словами ни на одном участке кода, где используются или каким-то чудесным образом могут появиться генерируемые пользователем данные (например, где-то какой-то функцией возвращаемые), необходимо контролировать тип данных, с которыми мы работаем и использовать принудительное приведение типов. Обычный intval() отсечет половину проблем в местах, где мы ждём id/флаг, а злоумышленник может подставить строку. Для строковых/текстовых параметров прекрасно работает mysql_real_escape_string().
И, да, первое, что я делаю, начиная писать что-то новое — выношу все функции по работе с БД в отдельный класс, в котором присутствуют все необходимые проверки перед выполнением запроса, начиная с самых простых и заканчивая самыми параноидальными.
Плюс к тому, подобный подход позволяет намного быстрее рефакторить код и в случае возникновения ошибки — править его только в одном месте и не искать по всему коду подобные уязвимые куски.
В данном случае хранимые процедуры БД не дают никакой прибавки к защищенности кода.
Поясню. Перед тем, как создвать динамический запрос нам так или иначе необходимо контролировать тип динамических данных. Иными словами ни на одном участке кода, где используются или каким-то чудесным образом могут появиться генерируемые пользователем данные (например, где-то какой-то функцией возвращаемые), необходимо контролировать тип данных, с которыми мы работаем и использовать принудительное приведение типов. Обычный intval() отсечет половину проблем в местах, где мы ждём id/флаг, а злоумышленник может подставить строку. Для строковых/текстовых параметров прекрасно работает mysql_real_escape_string().
И, да, первое, что я делаю, начиная писать что-то новое — выношу все функции по работе с БД в отдельный класс, в котором присутствуют все необходимые проверки перед выполнением запроса, начиная с самых простых и заканчивая самыми параноидальными.
Плюс к тому, подобный подход позволяет намного быстрее рефакторить код и в случае возникновения ошибки — править его только в одном месте и не искать по всему коду подобные уязвимые куски.
0
Ну начнем с того что десктопные приложения тоже работают с базами данных, и не все используют Perl, PHP и тд. И если запрос к базе можно перехватить, то его можно и поправить, в случае в процедурами мы не раскрываем схемы базы, логики запросов и тд. А вот в случае запроса мы позволяем менять запрос как угодно.
Как проще вытащить все поля, в том числе и пароли пользователей: если у нас процедура getUserList (которая вытаскивает id, name), или запрос, который выглядит как SELECT id, name FROM users. Я думаю правка «id, name» на "*" проще чем подобрать имя таблицы. Инъекция может быть на любой стадии, от формирования запроса, до передачи SQL серверу.
А дополнительная защита когда пользователь входит в базу под своим системным пользователем, у которого права определяются в самой процедуре. Т.е. даже имея имя, пароль пользователя (не рута) и имея имя процедуры ничего секретного вытащить не сможем
Как проще вытащить все поля, в том числе и пароли пользователей: если у нас процедура getUserList (которая вытаскивает id, name), или запрос, который выглядит как SELECT id, name FROM users. Я думаю правка «id, name» на "*" проще чем подобрать имя таблицы. Инъекция может быть на любой стадии, от формирования запроса, до передачи SQL серверу.
А дополнительная защита когда пользователь входит в базу под своим системным пользователем, у которого права определяются в самой процедуре. Т.е. даже имея имя, пароль пользователя (не рута) и имея имя процедуры ничего секретного вытащить не сможем
0
Повторюсь, все зависит от задачи и степени важности данных, если это форум, разумеется не стоит впадать в пароною, а вот если мы имеем дело с финансами, то тут стоит подумать ото всех степенях защиты, в том числе и правки исполняемого приложения в памяти, но это уже тема отдельной статьи)
0
Не думаю, что кого-то удивлю, если скажу, что финансовые системы тоже могут работать на Perl и PHP.
Действительно, если существует возможность перехвата и модификации запроса на уровне приложение->БД, то логично будет использовать хранимые процедуры. Но это уже, скорее, больше относится к типу атак MitM, чем к SQL-Injection.
Другое дело — мне тяжело представить такой случай. Возможно потому, что прикладным программированием я не занимался уже несколько лет, всё больше web да интранет.
P.S. Почему-то вспомнилась эпическая байка про программистов PЖД, которые гоняют всю базу целиком между клиентом и сервером ^^
Действительно, если существует возможность перехвата и модификации запроса на уровне приложение->БД, то логично будет использовать хранимые процедуры. Но это уже, скорее, больше относится к типу атак MitM, чем к SQL-Injection.
Другое дело — мне тяжело представить такой случай. Возможно потому, что прикладным программированием я не занимался уже несколько лет, всё больше web да интранет.
P.S. Почему-то вспомнилась эпическая байка про программистов PЖД, которые гоняют всю базу целиком между клиентом и сервером ^^
0
А так-же выключайте вывод логов ошибок клиенту, из которых можно получить представление о структуре базы и используйте небанальные названия таблиц: например придумайте некий префикс и добаляйте ко всем осмысленным названиям: someprefix-user, someprefix-profile,…
+2
Особой пользы от использования «небанальных названий таблиц нет», ибо на большинстве БД можно читать служебные базы данных, которые содержат информацию о структуре, и редкий админ сейчас закрывает к ним доступ.
А вообще тема сисек не раскрыта. Уязвимыми могут быть не только «данные введенные пользователем», а так же куки, юзер агенты и прочие данные передаваемые в http headers, и даже, как бы странно это не звучало, параметры сессии.
А вообще тема сисек не раскрыта. Уязвимыми могут быть не только «данные введенные пользователем», а так же куки, юзер агенты и прочие данные передаваемые в http headers, и даже, как бы странно это не звучало, параметры сессии.
0
я бы ещё добавил «храните пароли в БД в зашифрованном виде с солью»
0
Могу сказать и другое, что magic_qoutes не всегда правильно. Использую модуль для отключения его на любом хостинге:
if (! get_magic_quotes_gpc() ) return 1;
while ( list($k,$v)=each($_POST) ) $_POST[$k]=stripslashes($v);
return 1;
Потому что если в коде уже жестко зашита поддержка addslashes или mysql_escpace_string, то под соусом magic_qoutes получается полная каша.
if (! get_magic_quotes_gpc() ) return 1;
while ( list($k,$v)=each($_POST) ) $_POST[$k]=stripslashes($v);
return 1;
Потому что если в коде уже жестко зашита поддержка addslashes или mysql_escpace_string, то под соусом magic_qoutes получается полная каша.
0
Проблема SQL-инъекций решается элементарно. Если вы юзаете ORM, написанный не-индусами — там скорее всего проблемы нет. Если же вы хотите писать запросами, быстренько пишем библиотечку для обработки параметров, которая сама будет все экранировать, типа такой:
$data = getAll('SELECT * FROM ?t WHERE id =? LIMIT 10', 'table1', $unsafe_id);
Подобная есть например у dklab (вроде называется DBSimple), хотя конечно лучше написать свою.
А все эти PDO, дотнеты — это муть.
$data = getAll('SELECT * FROM ?t WHERE id =? LIMIT 10', 'table1', $unsafe_id);
Подобная есть например у dklab (вроде называется DBSimple), хотя конечно лучше написать свою.
А все эти PDO, дотнеты — это муть.
-3
>Подобная есть например у dklab (вроде называется DBSimple), хотя конечно лучше написать свою
Чем лучше? На мой взгляд, всегда лучше использовать популярную стороннюю библиотеку, которая хорошо протестирована (речь не конкретно про библиотеку от dklab — не использовал, оценить не могу), чем изобретать велосипед.
> А все эти PDO, дотнеты — это муть.
Сильно :)
Чем лучше? На мой взгляд, всегда лучше использовать популярную стороннюю библиотеку, которая хорошо протестирована (речь не конкретно про библиотеку от dklab — не использовал, оценить не могу), чем изобретать велосипед.
> А все эти PDO, дотнеты — это муть.
Сильно :)
0
Пацан к успеху идет, не мешайте :)
+4
да я только потом заметил, удалил бы комментарий, если бы мог :)
+1
Ой, у вас прям паранойя. Цифра в профиле ничего не значит и ничего не говорит о личности автора.
0
> А все эти PDO, дотнеты — это муть.
> Сильно :)
Ну не расписывать же мне на 2 страницы конкретные примеры, где использование PDO требует кучу писанины. Посмотрите, как там сделан например биндинг переменных — уродливо и неуклюже, нужно отдельный statement для каждой переменной. Также там доступен всего лишь один плейсхолдер. Я посмотрел, как он устроен, прикинул, сколько лишней писанины придется делать, если его использвоать, и сделал краткий вывод («муть»), не верите — идите, пользуйтесь, только потом не жалуйтесь, что неудобно, и что у вас лапша, а не код полчается. Мне например морально тяжело видеть, когда для простых вещей приходится писать кучу строчек, или что еще хуже, копипастить код из файла в файл.
И не писать же всякие набившую оскомину примеры неудачного синтаксиса из C# вроде:
SomeSpecialList list = new SomeSpecialList();
и так далее.
> Сильно :)
Ну не расписывать же мне на 2 страницы конкретные примеры, где использование PDO требует кучу писанины. Посмотрите, как там сделан например биндинг переменных — уродливо и неуклюже, нужно отдельный statement для каждой переменной. Также там доступен всего лишь один плейсхолдер. Я посмотрел, как он устроен, прикинул, сколько лишней писанины придется делать, если его использвоать, и сделал краткий вывод («муть»), не верите — идите, пользуйтесь, только потом не жалуйтесь, что неудобно, и что у вас лапша, а не код полчается. Мне например морально тяжело видеть, когда для простых вещей приходится писать кучу строчек, или что еще хуже, копипастить код из файла в файл.
И не писать же всякие набившую оскомину примеры неудачного синтаксиса из C# вроде:
SomeSpecialList list = new SomeSpecialList();
и так далее.
0
А я вот нихрена не понял, при чем здесь списки из дотнетов. Такое ощущение, что вы не шарите, что:
— во-первых, то, что вы привели, это c#, а не .net. Пишите на boo/Nemerle/IronPython/PhalangerPHP, и будет вам счастье на .net с любимым синтаксисом, например;
-во-вторых, тот же c# на месте не стоит, если чо:
var list = new SomeSpecialList(); // an example of wrists-friendly way ;)
— во-первых, то, что вы привели, это c#, а не .net. Пишите на boo/Nemerle/IronPython/PhalangerPHP, и будет вам счастье на .net с любимым синтаксисом, например;
-во-вторых, тот же c# на месте не стоит, если чо:
var list = new SomeSpecialList(); // an example of wrists-friendly way ;)
-1
> Пишите на boo/Nemerle/IronPython/PhalangerPHP, и будет вам счастье на .net с любимым синтаксисом, например;
Ага, и по RDP на сервер коннектиться, чтобы IIS черехз ГУИ настраивать — счастья то! Что только люди не придумают, лишь бы извратиться.
> во-вторых, тот же c# на месте не стоит, если чо:
Ну вот, можно порадоваться за шарпщиков, чуть меньше теперь у них голова болеть будет.
Ага, и по RDP на сервер коннектиться, чтобы IIS черехз ГУИ настраивать — счастья то! Что только люди не придумают, лишь бы извратиться.
> во-вторых, тот же c# на месте не стоит, если чо:
Ну вот, можно порадоваться за шарпщиков, чуть меньше теперь у них голова болеть будет.
0
> Чем лучше? На мой взгляд, всегда лучше использовать популярную стороннюю библиотеку, которая хорошо протестирована (речь не конкретно про библиотеку от dklab — не использовал, оценить не могу), чем изобретать велосипед.
Тем, что на мой взгляд, содержит несколько даром ненужных функций, абсолютно неправильный подход к кешированию (кеширование рез-в запросов, это чушь, MySQL умеет делать это намного лучше), и ограниченнный набор плейсхолдеров, мне того, что есть не хватает. Кроме того, там вроде нет lazy connect. Зачем мне кривое и громоздкое, пусть и протестированное решение?
Ах да, еще по поводу «протестированности»: это не так. Я использовал как-то их библиотеку. где-то год назад. Уже тогда. для нормальной работы под PHP5, без ваарнингов и нотисов, пришлось руками ее патчить. Полгода спустя, с новой версией PHP − опять проблемы, пусть я их и решил за 20 минут, но все же.
Кроме того, я коду, который я лично написал, доверяю куда как больше, чем неизвестно кем написанному (да еще и как выясняется, устаревшему).
Тем, что на мой взгляд, содержит несколько даром ненужных функций, абсолютно неправильный подход к кешированию (кеширование рез-в запросов, это чушь, MySQL умеет делать это намного лучше), и ограниченнный набор плейсхолдеров, мне того, что есть не хватает. Кроме того, там вроде нет lazy connect. Зачем мне кривое и громоздкое, пусть и протестированное решение?
Ах да, еще по поводу «протестированности»: это не так. Я использовал как-то их библиотеку. где-то год назад. Уже тогда. для нормальной работы под PHP5, без ваарнингов и нотисов, пришлось руками ее патчить. Полгода спустя, с новой версией PHP − опять проблемы, пусть я их и решил за 20 минут, но все же.
Кроме того, я коду, который я лично написал, доверяю куда как больше, чем неизвестно кем написанному (да еще и как выясняется, устаревшему).
0
потому что вы работаете один.
А вот я лично вашему коду доверяю меньше, чем сторонней известной библиотеке =) так-то
А вот я лично вашему коду доверяю меньше, чем сторонней известной библиотеке =) так-то
+1
Разрешите не согласиться. Почему PDO — это муть, когда во-первых в них уже реализовано именно то, что вы описываете, да еще и на более низком уровне (а значит быстрее), а во-вторых последнее время PDO используются во многих ORM (см. Doctrine, Propel).
0
Потому что реализовано это плохо и неудобно. Я хочу, чтобы было так:
> $top_posts = getAll('SELECT id, title, body FROM ?_posts WHERE category =? AND author IN (?a) LIMIT 10 ', $category, $authors);
> $comment = array(array('author' => 'me', 'title' => '...', 'body' => '...'), array('author' => 'me2', 'title' => '...', 'body' => '...'));
> do('INSERT INTO ?t ?insert', 'commects', $comments);
А они предлагают уродливый объект с неудобным интерфейсом, ну-ка, сколько мне придется лапши написать для решения той же задачи? Вот то-то. Проектируют не для людей, а для индусов (которым пофиг что писать, лишь бы копеечку заплатили), ни один образованный человек этим пользоваться не захочет.
> $top_posts = getAll('SELECT id, title, body FROM ?_posts WHERE category =? AND author IN (?a) LIMIT 10 ', $category, $authors);
> $comment = array(array('author' => 'me', 'title' => '...', 'body' => '...'), array('author' => 'me2', 'title' => '...', 'body' => '...'));
> do('INSERT INTO ?t ?insert', 'commects', $comments);
А они предлагают уродливый объект с неудобным интерфейсом, ну-ка, сколько мне придется лапши написать для решения той же задачи? Вот то-то. Проектируют не для людей, а для индусов (которым пофиг что писать, лишь бы копеечку заплатили), ни один образованный человек этим пользоваться не захочет.
+1
странных вещей ты хочешь %-) а я хочу так:
$top_posts = $db->query( 'SELECT id, title, body FROM posts WHERE category = ', $category, ' AND author IN ', $authors, ' LIMIT 10 ' )->row;
$top_posts = $db->query( 'SELECT id, title, body FROM posts WHERE category = ', $category, ' AND author IN ', $authors, ' LIMIT 10 ' )->row;
0
$top_posts = $db->query( 'SELECT id, title, body FROM posts WHERE category = ', $category, ' AND author IN ', $authors, ' LIMIT 10 ' )->table;
разумеется
разумеется
0
Интересный подход, не сталкивался с таким. Но способ с плейсхолдерами имеет то преимущество, что 1) если запрос большой, с многими параметрами, он читабельнее и не превращается в кашу из кавычек и запятых и 2) плейсхолдеры могут быть разных типов, а с вашим подходом это не сделать :)
+1
разбиваем на строки и всё получается красиво:
$authors= $db->query(' select uri, title from thing where uri in ( select object from link where relation = "?rel:author" and subject = ',$uri,' ) ')->rows;
0
а каких типов тебе не хватает?
0
Вот примерный список:
— стандартный строковой плейсхолдер
— числовой (не добавляет кавычек вокруг, используется например в LIMIT ?d, ?d)
— плейсхолдер-имя таблицы (к нему авт-ки доб. префикс, я использую префиксы для имен таблиц)
— плейсхолдер, делающий из массива строк список через запятую
— плейсхолдер, вставляющий префикс таблицы
— плейсхолдер для идентификатора (у них могут быть другие правила экранирования и другой вид кавычек)
Ну и пара «монстров» — плейсхолдеров, один для создания INSERT-выражения с кучей скобок из массива, второй, для выражения, которое используется в UPDATE… SET…
И вроде специальный плейсхолдер, для добавления имени таблицы (в сложных JOIN-запросах например) перед именами полей.
Вообще, стремлюсь перейти на использовние моделей и Table Gateway (и запросы писать руками не буду), так что того, что у меня давно написано, вполне хватает.
— стандартный строковой плейсхолдер
— числовой (не добавляет кавычек вокруг, используется например в LIMIT ?d, ?d)
— плейсхолдер-имя таблицы (к нему авт-ки доб. префикс, я использую префиксы для имен таблиц)
— плейсхолдер, делающий из массива строк список через запятую
— плейсхолдер, вставляющий префикс таблицы
— плейсхолдер для идентификатора (у них могут быть другие правила экранирования и другой вид кавычек)
Ну и пара «монстров» — плейсхолдеров, один для создания INSERT-выражения с кучей скобок из массива, второй, для выражения, которое используется в UPDATE… SET…
И вроде специальный плейсхолдер, для добавления имени таблицы (в сложных JOIN-запросах например) перед именами полей.
Вообще, стремлюсь перейти на использовние моделей и Table Gateway (и запросы писать руками не буду), так что того, что у меня давно написано, вполне хватает.
+1
> числовой (не добавляет кавычек вокруг, используется например в LIMIT ?d, ?d)
зачем? субд сама приведёт тип
> плейсхолдер, делающий из массива строк список через запятую
зачем? достаточно просто передать массив и он сериализуется в такой вот список
> плейсхолдер-имя таблицы (к нему авт-ки доб. префикс, я использую префиксы для имен таблиц)
> плейсхолдер, вставляющий префикс таблицы
это не плейсхолдер, а скорее макрос, ибо не требует передачи данных в каждом запросе
> плейсхолдер для идентификатора (у них могут быть другие правила экранирования и другой вид кавычек)
лучше не использовать идентификаторы в которых нужно что-то экранировать, ибо тем самым создаём себе геморрой на ровном месте
> Ну и пара «монстров» — плейсхолдеров, один для создания INSERT-выражения с кучей скобок из массива, второй, для выражения, которое используется в UPDATE… SET…
для этого лучше использовать вообще отдельный метод, ибо тут нужно не только формировать запрос, но и разбивать его на части, если он получается слишком длинным
> И вроде специальный плейсхолдер, для добавления имени таблицы (в сложных JOIN-запросах например) перед именами полей.
не надо делать сложных джойнов, пожалейте базу %-)
зачем? субд сама приведёт тип
> плейсхолдер, делающий из массива строк список через запятую
зачем? достаточно просто передать массив и он сериализуется в такой вот список
> плейсхолдер-имя таблицы (к нему авт-ки доб. префикс, я использую префиксы для имен таблиц)
> плейсхолдер, вставляющий префикс таблицы
это не плейсхолдер, а скорее макрос, ибо не требует передачи данных в каждом запросе
> плейсхолдер для идентификатора (у них могут быть другие правила экранирования и другой вид кавычек)
лучше не использовать идентификаторы в которых нужно что-то экранировать, ибо тем самым создаём себе геморрой на ровном месте
> Ну и пара «монстров» — плейсхолдеров, один для создания INSERT-выражения с кучей скобок из массива, второй, для выражения, которое используется в UPDATE… SET…
для этого лучше использовать вообще отдельный метод, ибо тут нужно не только формировать запрос, но и разбивать его на части, если он получается слишком длинным
> И вроде специальный плейсхолдер, для добавления имени таблицы (в сложных JOIN-запросах например) перед именами полей.
не надо делать сложных джойнов, пожалейте базу %-)
0
> > числовой (не добавляет кавычек вокруг, используется например в LIMIT ?d, ?d)
зачем? субд сама приведёт тип
Гм… с нестрогой типизацией в PHP явно указывать, что мол, тут число, а тут строка, мне как-то надежнее, чем потом по коду бегать и писать intval()/strval(). Хотя можно и так.
> > плейсхолдер, делающий из массива строк список через запятую
> зачем? достаточно просто передать массив и он сериализуется в такой вот список
Но ведь бывают список строк, а бывает например список идентификаторов (например, список полей для SELECT). Тут надо 2 плейсхолдера.
> лучше не использовать идентификаторы в которых нужно что-то экранировать, ибо тем самым создаём себе геморрой на ровном месте
Зато я знаю, что даже если я возьму имя идентификатора из необработанных пользвотельских данных, ничего страшного не случится.
>> Ну и пара «монстров» — плейсхолдеров, один для создания INSERT-выражения с кучей скобок из массива, второй, для выражения, которое используется в UPDATE… SET…
> для этого лучше использовать вообще отдельный метод, ибо тут нужно не только формировать запрос, но и разбивать его на части, если он получается слишком длинным
Насчет «на части» — да, здравая мысль, но я пока с такими огромными запросами не сталкивался. Но все же плейсходер для превращения ассоц. массива в набор вида field = 'value' and field2 = 'value' довольно-таки удобен.
Вот, нашел у себя еще один плейсхолдер, «рекурсивный», ?(), он берет переданный массив, содержащий кусок запроса с плейсхолдерами и параметры, интерпретирует его и вставляет полученный результат :) Годится для использования с ORM, где запрос часто составляется кусками.
> > И вроде специальный плейсхолдер, для добавления имени таблицы (в сложных JOIN-запросах например) перед именами полей.
> не надо делать сложных джойнов, пожалейте базу %-)
Ну мало ли, вдруг понадобится.
зачем? субд сама приведёт тип
Гм… с нестрогой типизацией в PHP явно указывать, что мол, тут число, а тут строка, мне как-то надежнее, чем потом по коду бегать и писать intval()/strval(). Хотя можно и так.
> > плейсхолдер, делающий из массива строк список через запятую
> зачем? достаточно просто передать массив и он сериализуется в такой вот список
Но ведь бывают список строк, а бывает например список идентификаторов (например, список полей для SELECT). Тут надо 2 плейсхолдера.
> лучше не использовать идентификаторы в которых нужно что-то экранировать, ибо тем самым создаём себе геморрой на ровном месте
Зато я знаю, что даже если я возьму имя идентификатора из необработанных пользвотельских данных, ничего страшного не случится.
>> Ну и пара «монстров» — плейсхолдеров, один для создания INSERT-выражения с кучей скобок из массива, второй, для выражения, которое используется в UPDATE… SET…
> для этого лучше использовать вообще отдельный метод, ибо тут нужно не только формировать запрос, но и разбивать его на части, если он получается слишком длинным
Насчет «на части» — да, здравая мысль, но я пока с такими огромными запросами не сталкивался. Но все же плейсходер для превращения ассоц. массива в набор вида field = 'value' and field2 = 'value' довольно-таки удобен.
Вот, нашел у себя еще один плейсхолдер, «рекурсивный», ?(), он берет переданный массив, содержащий кусок запроса с плейсхолдерами и параметры, интерпретирует его и вставляет полученный результат :) Годится для использования с ORM, где запрос часто составляется кусками.
> > И вроде специальный плейсхолдер, для добавления имени таблицы (в сложных JOIN-запросах например) перед именами полей.
> не надо делать сложных джойнов, пожалейте базу %-)
Ну мало ли, вдруг понадобится.
+1
> потом по коду бегать и писать intval()/strval()
это зачем? о0
> Но ведь бывают список строк, а бывает например список идентификаторов (например, список полей для SELECT). Тут надо 2 плейсхолдера.
implode( ',', $ids )
> Зато я знаю, что даже если я возьму имя идентификатора из необработанных пользвотельских данных, ничего страшного не случится.
поведение программы будет слабопредсказуеммым. в частности, в некоторых случаях пользователь сможет запросто устроить дос сравнительно безобидными запросами.
> Насчет «на части» — да, здравая мысль, но я пока с такими огромными запросами не сталкивался.
я тоже, но разрабатывая интерфейс нужно сразу быть готовым к переделкам
> Вот, нашел у себя еще один плейсхолдер, «рекурсивный», ?(), он берет переданный массив, содержащий кусок запроса с плейсхолдерами и параметры, интерпретирует его и вставляет полученный результат :) Годится для использования с ORM, где запрос часто составляется кусками.
идиты! 0_0" рекурсия — зло! если разрывать запрос на части, то теряется его связность и повышается риск сборки его в неправильном порядке и геморроя с отладкой.
> Ну мало ли, вдруг понадобится.
всё-равно не понятно, чем тут плейсхолдеры помогут
это зачем? о0
> Но ведь бывают список строк, а бывает например список идентификаторов (например, список полей для SELECT). Тут надо 2 плейсхолдера.
implode( ',', $ids )
> Зато я знаю, что даже если я возьму имя идентификатора из необработанных пользвотельских данных, ничего страшного не случится.
поведение программы будет слабопредсказуеммым. в частности, в некоторых случаях пользователь сможет запросто устроить дос сравнительно безобидными запросами.
> Насчет «на части» — да, здравая мысль, но я пока с такими огромными запросами не сталкивался.
я тоже, но разрабатывая интерфейс нужно сразу быть готовым к переделкам
> Вот, нашел у себя еще один плейсхолдер, «рекурсивный», ?(), он берет переданный массив, содержащий кусок запроса с плейсхолдерами и параметры, интерпретирует его и вставляет полученный результат :) Годится для использования с ORM, где запрос часто составляется кусками.
идиты! 0_0" рекурсия — зло! если разрывать запрос на части, то теряется его связность и повышается риск сборки его в неправильном порядке и геморроя с отладкой.
> Ну мало ли, вдруг понадобится.
всё-равно не понятно, чем тут плейсхолдеры помогут
0
>>> Но ведь бывают список строк, а бывает например список идентификаторов (например, список полей для SELECT). Тут надо 2 плейсхолдера.
> implode( ',', $ids )
Фи, индусокод. Зачем тогда вообще плейсхолдеры делать. Не говоря о том, что вы забыли про mysql_real_escape_string, а?
> рекурсия — зло! если разрывать запрос на части, то теряется его связность и повышается риск сборки его в неправильном порядке и геморроя с отладкой.
Тем не менее, бывают случаи, когда запрос собирается из кусков, например добавляются условия или сортировка. или еще что-нибудь.
> implode( ',', $ids )
Фи, индусокод. Зачем тогда вообще плейсхолдеры делать. Не говоря о том, что вы забыли про mysql_real_escape_string, а?
> рекурсия — зло! если разрывать запрос на части, то теряется его связность и повышается риск сборки его в неправильном порядке и геморроя с отладкой.
Тем не менее, бывают случаи, когда запрос собирается из кусков, например добавляются условия или сортировка. или еще что-нибудь.
0
> Зачем тогда вообще плейсхолдеры делать.
у меня нет никаких плейсхолдеров.
> Не говоря о том, что вы забыли про mysql_real_escape_string, а?
нет, просто не надо данные от пользователя использовать в качестве идентификаторов. но даже если очень хочется — этапе ремапинга имён весь мусор отфильтруется.
> Тем не менее, бывают случаи, когда запрос собирается из кусков, например добавляются условия или сортировка. или еще что-нибудь.
в этом случае динамически собирается правильная структура, а потом она уже сериализуется с экранизацией параметров
у меня нет никаких плейсхолдеров.
> Не говоря о том, что вы забыли про mysql_real_escape_string, а?
нет, просто не надо данные от пользователя использовать в качестве идентификаторов. но даже если очень хочется — этапе ремапинга имён весь мусор отфильтруется.
> Тем не менее, бывают случаи, когда запрос собирается из кусков, например добавляются условия или сортировка. или еще что-нибудь.
в этом случае динамически собирается правильная структура, а потом она уже сериализуется с экранизацией параметров
0
> нет, просто не надо данные от пользователя использовать в качестве идентификаторов. но даже если очень хочется — этапе ремапинга имён весь мусор отфильтруется.
> в этом случае динамически собирается правильная структура, а потом она уже сериализуется с экранизацией параметров
Ну вот :) У нас разные подходы. Мне просто удобнее отдать данные, как есть, а код работы с БД пусть сам их там экранирует внутри, я *принципиально* не хочу вручную вызывать escape-функции и проверять, опасные это значения или нет. Я просто ставлю знак вопроса, и знаю, что туда будут подставлены данные в правильном виде.
> в этом случае динамически собирается правильная структура, а потом она уже сериализуется с экранизацией параметров
Ну вот :) У нас разные подходы. Мне просто удобнее отдать данные, как есть, а код работы с БД пусть сам их там экранирует внутри, я *принципиально* не хочу вручную вызывать escape-функции и проверять, опасные это значения или нет. Я просто ставлю знак вопроса, и знаю, что туда будут подставлены данные в правильном виде.
0
позиционные плейсхолдеры при больших количествах сильно страдают в плане наглядности.
0
Не сталкивался с такими мега-запросами :). Впрочем, куча переменных, кавычек и запятых тоже будет не очень смотреться.
0
Здесь кусок кода, предложенный Джоном Ли:
$username = $_POST['username'];
$password = $_POST['password'];
if (!get_magic_quotes_gpc()) {
$username = addslashes($username);
$password = addslashes($password);
}
Это неправильный совет.
1) Addslashes некорректно работает с мультибайтовыми кодировками; мало того, что оно испортит данные и в случае, например, с японской кодировкой Shift-JIS, вполне возможно проведение sql-инъекции для вышеприведенного кода. Magic quotes по той же причине лучше всего отключать, если это невозможно (недоступны htaccess/php.ini) — то использовать stripslashes и задуматься о смене хостинга.
2) На самом деле, поведение addslashes еще и зависит от настройки magic_quotes_sybase (по сути, magic_quotes включает автоприменение addslashes к входным get/post/cookie данным), что может оказаться отдельным сюрпризом.
3) Для каждой СУБД есть своя функция экранирования, реализованная в соответствующией клиентской библиотеке. Например, для mysql это mysql_real_escape_string.
4) Конечно же, еще лучше использовать плейсхолдеры — PDO или иную реализацию.
+3
Спасибо. Мы как раз улучшали процедуры по безопасности создаваемых сайтов, информация пригодится.
Кстати, заметил, что по безопасности сайтов очень редко пишут на Хабре, а ведь тема очень интересная, если учесть, сколько тут разработчиков!..
Кстати, заметил, что по безопасности сайтов очень редко пишут на Хабре, а ведь тема очень интересная, если учесть, сколько тут разработчиков!..
+3
хорошая утилита для сканирования (своих конечно) сайтов на SQL injection — sqlmap
sqlmap.sourceforge.net/
sqlmap.sourceforge.net/
0
bind variables! да большинство программистов вообще не слышали про это.
А если учесть новомодное декларативное программирование, где большинство запросов в базу лежит в исходном коде вот так:
select {field_list} from {table} where {where_Clause}
то блин мы имеем то, что мы имеем. :(
А если учесть новомодное декларативное программирование, где большинство запросов в базу лежит в исходном коде вот так:
select {field_list} from {table} where {where_Clause}
то блин мы имеем то, что мы имеем. :(
0
Бывают проблемы, которые не так легко фиксаются с помощью bind.
Во-первых, если давно написанный кривой интерфейс подразумевает передачу строки с SQL-запросом в качестве параметра или возвращаемого значения. Типа такого (Java):
Пусть такой интерфейс использует некий класс, который наполняет коллекцию данными из произвольной SQL-базы. Интерфейс мог быть реализован 20 раз для разных сторонних баз и вдобавок опубликован с целью, чтобы сторонние разработчики писали плагины (и они их написали!). А тут вдруг задумались об SQL-инъекциях. Что делать? Оборвать руки проектировщику интерфейса, правильно. Только проблему это не решит.
Второй проблемный случай, когда мы генерируем запрос с непостоянным количеством подставляемых полей. Нам это требовалось при массовой вставке данных в базу. Делать prepare(«INSERT blahblah VALUES(?,?,?)») и потом тысячу раз execute(var1, var2, var3) существенно медленнее, чем загнать всё в один длинный INSERT. Особенно разница ощутима, когда база по сети и далеко. Мы сделали классик на перле, который инкапсулировал множественную вставку в базу, используя функцию $dbh->quote(), которая умеет экранировать спецсимволы в запросах и делает это правильно для любой СУБД, для который установлен перловый драйвер. Когда набиралась максимально допустимая длина запроса, проталкивали в сокет и начинали формировать новый запрос. Прекрасно работало и с MySQL, и с Oracle, с UTF-8 тоже. К сожалению, я не нашёл аналога $dbh->quote() в Java (плохо искал?). Ручная реализация экранирования кажется ненадёжной, так как некоторые СУБД требуют удвоение кавычки, а некоторые — добавление слэша перед ней.
Во-первых, если давно написанный кривой интерфейс подразумевает передачу строки с SQL-запросом в качестве параметра или возвращаемого значения. Типа такого (Java):
interface SQLDatabaseDriver { public String getNamesQuery(); public String getValueByNameQuery(String value); }; class SomeSpecificDatabaseDriver implements SQLDatabaseDriver { public String getNamesQuery() { return "SELECT name FROM tbl";} public String getValueByNameQuery(String name) { return "SELECT value FROM tbl WHERE name='"+name+"'"; } };
Пусть такой интерфейс использует некий класс, который наполняет коллекцию данными из произвольной SQL-базы. Интерфейс мог быть реализован 20 раз для разных сторонних баз и вдобавок опубликован с целью, чтобы сторонние разработчики писали плагины (и они их написали!). А тут вдруг задумались об SQL-инъекциях. Что делать? Оборвать руки проектировщику интерфейса, правильно. Только проблему это не решит.
Второй проблемный случай, когда мы генерируем запрос с непостоянным количеством подставляемых полей. Нам это требовалось при массовой вставке данных в базу. Делать prepare(«INSERT blahblah VALUES(?,?,?)») и потом тысячу раз execute(var1, var2, var3) существенно медленнее, чем загнать всё в один длинный INSERT. Особенно разница ощутима, когда база по сети и далеко. Мы сделали классик на перле, который инкапсулировал множественную вставку в базу, используя функцию $dbh->quote(), которая умеет экранировать спецсимволы в запросах и делает это правильно для любой СУБД, для который установлен перловый драйвер. Когда набиралась максимально допустимая длина запроса, проталкивали в сокет и начинали формировать новый запрос. Прекрасно работало и с MySQL, и с Oracle, с UTF-8 тоже. К сожалению, я не нашёл аналога $dbh->quote() в Java (плохо искал?). Ручная реализация экранирования кажется ненадёжной, так как некоторые СУБД требуют удвоение кавычки, а некоторые — добавление слэша перед ней.
0
Как же я все-таки не люблю, когда мешают в одном файле разные языки. Предпочитаю sql хранить отдельно. Идеальным считаю вообще использование хранимых процедур и функций. Особенно это хорошо, когда есть отдельный ДБА или ДБ-программер.
+1
У меня не нашёл SQL injection в тестовом примере.
Значение в форме такое:
$result = mysql_query($sql);
echo '
}
echo '
?>
Значение в форме такое:
<form method="post"> <input type="text" name="q" /> </form> <?php if (isset($_POST['q']) and !empty($_POST['q'])) { mysql_connect('localhost', 'root', ''); mysql_select_db('ruzievkz'); $sql = 'SELECT * FROM route WHERE route_id='.$_POST['q']; echo '<pre>'; print_r($sql); echo '';
$result = mysql_query($sql);
echo '
'; var_dump(mysql_fetch_object($result)); echo '';
}
echo '
'; var_dump($_POST); echo '';
?>
0
Пардон, случайно нажал Ctrl+Enter и отправилась каша. =\
Плагин вот чего не обнаружил:
Тест собственно такой: 9 OR route_id = 1 AND route_id = 2 OR route_id = 3
Форма и обрабочик: pastebin.com/h4rKFxcs
Плагин вот чего не обнаружил:
Тест собственно такой: 9 OR route_id = 1 AND route_id = 2 OR route_id = 3
Форма и обрабочик: pastebin.com/h4rKFxcs
0
Использование рекурсивной проверки всех данных ( включая ENV, _SERVER и прочите ) в начале выполнения программы спасает от инъекций. Лет пять назад писал модуль, который банальным инклудом вставлялся в любой конфиг.пхп. Вроде до сих пор где-то работает :)
0
>>> Как вы видите, есть много решений (и прежде всего все простые) которые вы можете предпринять
Не можем, ибо это не решения, а ужас самый настоящий. От одного addslashes() аж передёрнуло.
Для избежания инъекций надо всего лишь понять что не данные пользователя мы вставляем в запрос, а «то нечто» что сервер воспримет за эти данные (а не за часть запроса)
То есть еогда мы пишем запрос к базе данных:
SELECT * FROM `table` WHERE `word`='какое-то слово';
Cамо «какое-то слово» не опасно. Опасно будет если мы оправим это «слово» в запрос! Надо отправлять не «это слово» а то что SQL сервер примет за это слово. Можно даже так:
SELECT * FROM `table` WHERE `word`=0x4841434b454420425920564c4144534f4e;
Данный пример упрощённый, в реальности mysql_real_escape_string справляется с этим вполне, более того есть не мало готовых классов для работы с БД которые сами составляют запросы (замечу в 99% web скриптов запросы более чем элементарны, в противном случае надо менять разработчика)
Не можем, ибо это не решения, а ужас самый настоящий. От одного addslashes() аж передёрнуло.
Для избежания инъекций надо всего лишь понять что не данные пользователя мы вставляем в запрос, а «то нечто» что сервер воспримет за эти данные (а не за часть запроса)
То есть еогда мы пишем запрос к базе данных:
SELECT * FROM `table` WHERE `word`='какое-то слово';
Cамо «какое-то слово» не опасно. Опасно будет если мы оправим это «слово» в запрос! Надо отправлять не «это слово» а то что SQL сервер примет за это слово. Можно даже так:
SELECT * FROM `table` WHERE `word`=0x4841434b454420425920564c4144534f4e;
Данный пример упрощённый, в реальности mysql_real_escape_string справляется с этим вполне, более того есть не мало готовых классов для работы с БД которые сами составляют запросы (замечу в 99% web скриптов запросы более чем элементарны, в противном случае надо менять разработчика)
0
+2
Sign up to leave a comment.
Профилактика SQL-инъекций