ElenaKosilova: DmitryArsentiev/ToBeContinued/mysqlinjection ...

Home Page | Каталог | Изменения | НовыеКомментарии | Пользователи | Регистрация | Вход:  Пароль:  

My SQL? injection


http://community.livejournal.com/ru_php/552359.html


Решил я тут систематизировать правила составления запросов, о которых столько говорю. На примере My SQL?.
Поскольку одна голова всяко хуже, чем миллион леммингов, то прошу высказываться.
Всё ли понятно написано, а – главное – все ли случаи учтены.


Основная задача – формализовать правила так, чтобы при их соблюдении достигалась 100%-я гарантия безопасности.
К примеру, для пхп скриптов такие правила написать довольно затруднительно – они получатся слишком длинными и конкретными, для каждого случая. Для SQL(My SQL?) же, как мне кажется, возможно составить набор ПРОСТЫХ правил, соблюдая которые можно гарантировать безопасность.


Правила эти делятся на две группы.
1. Все значения полей, которые используются в запросе, дожны быть
– заключены в кавычки
– к ним должна быть применена функция mysql_real_escape_string()
2. Все остальные части запроса, которые меняются в зависимости от выбора пользователя, ни в коем случае не должны подставляться напрямую, а только в виде результатов вычислений.
То есть:
– Если параметр имеет строковый тип, то все возможные значения должны быть прописаны в скрипте заранее. И уже из них выбирается нужное на основании выбора пользователя.
– если параметр является числом, то обязан получаться в результате арифметического выражения.
К примеру, все поля, по которым производится сортировка, должны быть прописаны в массиве, индексами которого являются значения, которые выбирает пользователь.
Цифры для лимита должны быть результатом арифметического выражения, и так далее.
То есть, повторюсь – операторы SQL не должны ни в коем случае попадать в запрос напрямую, а могут только служить исходными данными для выражения, которое и вернёт нужный параметр. То есть, скрипт не должен их проверять, и после проверки подставлять. В этом подходе изначально заложена ошибка. А должно быть именно только так, чтобы строка, переданная от пользователя, ни в каком виде не попадала в запрос, как оператор. Подставлять можно только значения полей, и только после обработки, описанной в п.1.
Речь во второй части, повторюсь, идёт об ОПЕРАТОРАХ, т.е. о любых частях запроса, которые не являются значениями полей.


Вторая часть правил получилась длинноватой, и это настораживает.


Upd.
Пока, по результатам обсуждения, выявлось две проблемы.
1. Надо чётче акцентировать внимание на первой паре правил, на том, что любые манипуляции с содержимым полей базы описываются этим несложным алгоритмом и НИКАКОЙ проблемы не представляют.
Что вся муть второй пары относится к остальным частям запроса. к именам полей, операторам и так далее.
Например, так:
«сначала надо чётко уяснить разницу между значениями полей и всеми остальными частями запроса».


2. Надо яснее пояснять вторую часть. Желательно – с примерами.
пример обработки многофакторного поиска:
if ($_GET['operation']=='or') $operation = “ or "; else $operation = “ and ";
$w=array();
if (!empty($_GET['rooms'])) $w[]="rooms='".mysql_real_escape_string($_GET['rooms'])."'";
if (!empty($_GET['space'])) $w[]="space='".mysql_real_escape_string($_GET['space'])."'";
if (!empty($_GET['price'])) $w[]="rooms='".mysql_real_escape_string($_GET['price'])."'";


if (count($w)) $where="WHERE «.implode($w,$operation); else $where='';
$query="select * from table $where";


3. так же, мне указали на то, что все эти рекомендации касаются чистого составления запросов, получения запроса в чистом виде и подстановки его в mysql_query
Для библиотек, которые предоставляют средства сборки запросов из кирпичиков, рекоменации отличаются.


(Добавить комментарий)

kocmoc
2005–12–04 01:49 pm (local) (ссылка) Отслеживать
«Если параметр имеет строковый тип, то все возможные значения должны быть прописаны в скрипте заранее. И уже из них выбирается нужное на основании выбора пользователя.»


Не совсем понимаю. Это как, если юзер региться на сайте, то он выбирает свой емайл из предложенного списка? :)


(Ответить)(Ветвь дискуссии)

phorror
2005–12–04 01:54 pm (local) (ссылка) Отслеживать
да, не совсем.
рекомендую перечитать ещё раз.
если не поможет – будем упрощать текст, для облегчения понимания людми, которые не в состоянии удержать в голове один абзац текста.


(Ответить)(Уровень выше) (Ветвь дискуссии)

kocmoc
2005–12–04 02:00 pm (local) (ссылка) Отслеживать
Да вижу-вижу уже, чувства юмора ни в тексте, ни у автора.


(Ответить)(Уровень выше) (Ветвь дискуссии)

_developer_
2005–12–04 02:06 pm (local) (ссылка) Отслеживать
Задавать вопрос не прочитав того, о чем собственно текст – это вы относите к Юмору?


(Ответить)(Уровень выше)

phorror
2005–12–04 02:21 pm (local) (ссылка) Отслеживать
юмора в тексте, кстати, да – не хватает.
будем над этим думать


(Ответить)(Уровень выше)

_developer_
2005–12–04 01:59 pm (local) (ссылка) Отслеживать
Рекоммендую поработать над подачей инйормации во втором абзаце.
Для тупых можно добавить примеры, аля


'SELECT... FROM... WHERE a="'.mysql_real_escape_string($value).'" LIMIT 1' – правильно
'SELECT... FROM... WHERE a="'.$value.'" LIMIT 1' – не правильно


И так далее, чтобы было наглядно видно ;)


(Ответить)(Ветвь дискуссии)

phorror
2005–12–04 02:08 pm (local) (ссылка) Отслеживать
над примерами подумаю.
но меня интересуют в первую очередь две вещи:
1. Всеобъемлющесть этих правил. Учитываются ли все взможные варианты?
К примеру, во всех руководствах упор делается только на первую часть, а про вторую как-то забывают. Я и сам грещшен.
вторая, собственно, и нуждается больше всего в формализации и проверке реальными задачами.
К примеру, когда у тебя скрипт должен выдавать один из 10000 файлов, все их в скрипте не пропишешь. Вот мне и интересно – есть ли такие случаи в запросах.
2. Доходчивость. (Она-то точно хромает, пытаюсь формулировать, а в диалоге это получается лучше всего)


(Ответить)(Уровень выше) (Ветвь дискуссии)

_developer_
2005–12–05 06:05 am (local) (ссылка) Отслеживать
когда у тебя скрипт должен выдавать один из 10000 файлов, все их в скрипте не пропишешь


Если речь идет о запросах – мы можем смело сделать селект, перегнать в массив, а потом смотреть, есть ли то, что пришло в запросе, в данной таблице.


Например если пересылалось имя поля – SHOW FIELDS FROM table; после чего гоним в массив и смотрим, есть ли в базе поле, соответствующее тому, что пришло. Но тоже не очень секурно конечно...


Где-то я видел толковую книгу по мусклу. Жаль что она мне попалась только вот недавно. Там очень хорошо описаны почти все возможные ситуации. Вспомню название – скажу.


(Ответить)(Уровень выше)

_developer_
2005–12–04 02:07 pm (local) (ссылка) Отслеживать
Кстати, почему бы тебе не кросс-постнуть в ru_mysql ?


(Ответить)(Ветвь дискуссии)

phorror
2005–12–04 02:17 pm (local) (ссылка) Отслеживать
Не знаю, даже.
Сам я там не подписан, и мне кажется, что все, кому интересна данная тема, прочтут и здесь.


(Ответить)(Уровень выше) (Ветвь дискуссии)

benadin
2005–12–05 06:07 pm (local) (ссылка) Отслеживать
В ru_mysql было бы правильнее :-)


(Ответить)(Уровень выше)

aire
2005–12–04 02:43 pm (local) (ссылка) Отслеживать
1. Стоит ли брать названия столбцов в ковычки(обратные)?


2. Поможет ли is_numeric для int?


3. Выражение «Если параметр имеет строковый тип, то все возможные значения должны быть прописаны в скрипте заранее. И уже из них выбирается нужное на основании выбора пользователя» действительно с первого прочтения вводит в ступор. Скорее всего именно фраза «все возможные».


(Ответить)(Ветвь дискуссии)

sb16
2005–12–04 03:01 pm (local) (ссылка) Отслеживать
1. ДА! Особенно если поля имеют имена типа text, date и т.д.
2. intval()!


(Ответить)(Уровень выше) (Ветвь дискуссии)

aire
2005–12–04 03:15 pm (local) (ссылка) Отслеживать
1) Сколько себя помню всегда писал имена полей в ковычках и не мог понять почему. Вот, сейчас вспомнил =)
2) Где-то я встречал просто домножение значения на еденичку. Сейчас проверил возможные варианты. Различия, как и ожидалось появились в значениях с плавающей точкой. C практической точки зрения даже не понятно пока, что лучше.


(Ответить)(Уровень выше) (Ветвь дискуссии)

sb16
2005–12–04 05:07 pm (local) (ссылка) Отслеживать
2. Если у нас float, то есть floatval. На сколько я понимаю, такой вариант будет быстрее всего работать, так как преобразование происходит только если переменная не число. И преобразование всегда в ноль, что IMHO очень удобно!


(Ответить)(Уровень выше)

phorror
2005–12–04 05:10 pm (local) (ссылка) Отслеживать
с практической точки зрения проще всего – забыть о существовании различных типов и лепить всё, как строки.


(Ответить)(Уровень выше) (Ветвь дискуссии)


(Комментарий удалён)

phorror
2005–12–05 06:28 pm (local) (ссылка) Отслеживать
Да. если говорить только о данных, о том, что относится к содержимому столбцов – да. тут всё просто и чётко.
Но проблема в том, что кроме стандартных операций с полями, в веб-приложении приходится модифицировать запрос.
а примеру – указывать поля, по которым мы ищем.
или подставлять значения в лимит.
или в ордер бай.
во всех этих случаях кавычки с искейпом проходят мимо


(Ответить)(Уровень выше) (Ветвь дискуссии)


(Комментарий удалён)

phorror
2005–12–05 07:13 pm (local) (ссылка) Отслеживать
спасибо, чувак, за твои советы.
но я это всё уже написал.
буду рад, если мне не придётся разжёвывать тебе свой пост дальше, а ты его асилишь самостоятельно.


(Ответить)(Уровень выше)

phorror
2005–12–04 05:13 pm (local) (ссылка) Отслеживать
1. Да, стоит. спасибо за напоминание.
2. следует уточнить, о каком пункте идёт речь – о первом или о втором.
ниже, в обсуждении, уже сбились на первый, а для первого вообще проблем нет.
для второго же, как я написал, нужна не проверка, а участие в выражении. это УНИВЕРСАЛЬНОЕ правило. в отличие от частного is_numeric для int.
3. попробую поправить


(Ответить)(Уровень выше)

hidden_4003
2005–12–04 03:41 pm (local) (ссылка) Отслеживать
насчёт 2) так если надо именно проверка то можно поидее пробовать так
if ($var === (int)$var)
а для is_numeric должна быть поддержка ctype, хотя я пока не сталкивался с её отсутствием где либо


(Ответить)(Ветвь дискуссии)

phorror
2005–12–04 05:15 pm (local) (ссылка) Отслеживать
Как раз моя мысль и заключается в том, что «проверка не надо».
что надо не проверку делать. а в выражение подставлять. числа проверить можно. а строки – уже нельзя. поэтому ОБЩЕЕ правило – не проверка, а подстановка в выражение.


(Ответить)(Уровень выше) (Ветвь дискуссии)

hidden_4003
2005–12–04 05:56 pm (local) (ссылка) Отслеживать
Отлично а как быть со случаем когда необходимы данные от пользователя ? или Вы рассматриваете только случаи выборки данных ?
При выборке согласен что необходимо работать по принципу «запретить всё, описать исключения».


(Ответить)(Уровень выше) (Ветвь дискуссии)

phorror
2005–12–04 10:27 pm (local) (ссылка) Отслеживать
все случаи в изложенных выше правилах рассмотрены.
Для лучшего понимания я бы попросил пример «данных от пользователя». что за данные, куда идут.
По получении примера я укажу правило, по которому их обрабатывать.


а я НЕ СОГЛАСЕН с принципом «запретить всё». Я считаю, что это порочная практика.


(Ответить)(Уровень выше) (Ветвь дискуссии)

hidden_4003
2005–12–04 11:34 pm (local) (ссылка) Отслеживать
Ну вот фраза:
– Если параметр имеет строковый тип, то все возможные значения должны быть прописаны в скрипте заранее. И уже из них выбирается нужное на основании выбора пользователя.
Т.е. я предполагаю имеются ввиду все возможные значения данного конкретного параметра, а если значения не будет в списке получается что входные данные не верны и на этом обработка завершается с ошибкой. Вот и получается что запрещено всё кроме того что описано.


«То есть, скрипт не должен их проверять, и после проверки подставлять.»
А как же быть если надо данные от пользователя получить? Имя и фамилию через форму допустим. Использоваьть базу имён и базу фамилий формировать массив и проверять ? :)


(Ответить)(Уровень выше) (Ветвь дискуссии)

phorror
2005–12–04 11:38 pm (local) (ссылка) Отслеживать
Имя и фамилия – это значение поля. Описывается первой парой правил.


а по поводу запрещения понял. согласен.


(Ответить)(Уровень выше) (Ветвь дискуссии)

hidden_4003
2005–12–04 11:43 pm (local) (ссылка) Отслеживать
Ага, понял. Извиняюсь, пропустил кусочек текста «1. Все значения полей, которые используются в запросе, дожны быть».


(Ответить)(Уровень выше) (Ветвь дискуссии)

phorror
2005–12–04 11:55 pm (local) (ссылка) [AD]
Уже второй. Значит, не так ясно написано, как надо.
будем исправлять


(Ответить)(Уровень выше)


(Комментарий удалён)

phorror
2005–12–05 06:29 pm (local) (ссылка) Отслеживать
я там, выше, уже написал.
кавычки недостаточны.
читайте ВНИМАТЕЛЬНО текст в посте.
кавычки – это только ПЕРВАЯ ПАРА правил.
читайте вторую


(Ответить)(Уровень выше) (Ветвь дискуссии)


(Комментарий удалён)

phorror
2005–12–05 07:15 pm (local) (ссылка) Отслеживать
логику программы на PHP мы здесь вообще не трогаем.
речь идёт только о My SQL?
проверки бывают нужны для предотвращения SQL-Injection!


(Ответить)(Уровень выше)

nach_berlin
2005–12–04 10:45 pm (local) (ссылка) Отслеживать
спасибо, мне эти рекомендации очень помогут
но лучше действительно все с примерами делать, а то пришлось дважды текст перечитать, чтобы врубиться как следует :-) хотя, это не слишком много


(Ответить)(Ветвь дискуссии)

phorror
2005–12–04 11:17 pm (local) (ссылка) Отслеживать
В первоначальном варианте текста примеры были.
Но тут такая беда. В том-то и отличие обобщающего правила от конкретного примера, что правило охватывает вообще все возможные варианты, а пример – только ОДИН частный случай.
Учитывая склонность большинства посетителей данного коммьюнити обращаться сразу к примеру (при его наличии), я просто боюсь, что общее правило до них не дойдёт и за ответом для другого частого случая они придут сюда...


А хотелось вывести именно свод общих правил. Для любых примеров.
Дело в том, что я в последнее время повадился говорить одну глупость, на тему «Не бывает SQL-инъекций, бывают неграмотные запросы». Имея ввиду именно работу с содержимым ячеек, первую пару правил. Там – да, всё просто. Но ведь существует ещё и работа с частями запроса. А здесь, действительно, строгих правил нет, и подставиться под инъекцию уже гораздо проще.


А примеры, как я понимаю, требуются такие:
первая пара:
$query="SELECT * FROM table WHERE name LIKE '".mysql_real_escape_string($_GET['name'])."%'";
вторая пара:
первое правило:
$limit_from=$page*$rows_per_page;
второе правило:
$sort_arr=(«name»=>"name","price"=>"price","avail"=>"avail");
if (isset($sort_arr[$sort])) $orderby=$sort_arr[$sort]); else $orderby="name";
предполагается, что переменные $page и $sort уже получены надлежащим образом из массива $_GET


(Ответить)(Уровень выше) (Ветвь дискуссии)

nach_berlin
2005–12–04 11:38 pm (local) (ссылка) Отслеживать
ну, мне кажется, что в любом случае будут сюда приходить за ответом


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


хотя, опять же, если без примеров, то начинаешь думать, что хотел сказать автор, придумывать свои примеры, и так материал усваивается лучше :-)


(Ответить)(Уровень выше) (Ветвь дискуссии)

phorror
2005–12–04 11:46 pm (local) (ссылка) Отслеживать
ну и как? примеры соответствуют?


вообще, на самом деле, примеров я, наверное, хотел от комментаторов. только не додумался явно об этом написать. Реальных примеров запросов. Ккак укладывающихся в эти правила, так и – что очень важно – не подходящие под них.


то у нас вообще, помимо значений полей, в запросе меняется по запросу юзера?
лимит.
ордер.
направление сортировки.
поля во where при сложном поиске.
пока всё укладывается.
что бывает ещё?


(Ответить)(Уровень выше) (Ветвь дискуссии)

nach_berlin
2005–12–04 11:54 pm (local) (ссылка) Отслеживать
я, если честно, со сложными задачами для sql пока нечасто сталкиваюсь, поэтому больше ничего не вспомню


а примеры да, соответствуют
я, правда, никак не приучу себя, что часто вместо switch лучше использовать массивы :-)


(Ответить)(Уровень выше) (Ветвь дискуссии)

phorror
2005–12–04 11:56 pm (local) (ссылка) Отслеживать
ага, спасибо.


(Ответить)(Уровень выше)

zexo
2005–12–05 12:46 pm (local) (ссылка) Отслеживать
http://dklab.ru/lib/Database_Placeholder/


(Ответить)(Ветвь дискуссии)

phorror
2005–12–05 12:53 pm (local) (ссылка) Отслеживать
Котеровская либа – далеко не единственная, и идалеко не лучшая реализация плейсхолдеров.
Не говоря уже о том, что она занимается реализацией именно тех принципов, которые здесь описаны. Это просто один из вариантов реализации. Не самый лучший.
Не говоря уже о том, что реализуется только первая пара правил.
а о второй всё равно нужно думать своей головой.


(Ответить)(Уровень выше)

praktikant
2005–12–05 02:10 pm (local) (ссылка) Отслеживать
Три основных правила для безопасности веб приложений.
1. Все что приложением получено должны быть отфильтровано.
Безопаснее черным список (все что получено – невалидно, кроме того, что я жду).
2. Всё что приложение отдает должно быть адекватно проэскэйпено.
3. 100% гарантии безопасности достигнуть нельзя.


Эти правила относятся не только к PHP<->My SQL?, но и вообще ко всем языкам и системам, которые для приложения являются внешними – и база данных и броузер клиента и вызов шелл и т.д.


(Ответить)(Ветвь дискуссии)

phorror
2005–12–05 02:26 pm (local) (ссылка) Отслеживать
Правила красивые – спору нет.
Но мне бы хотелось вывести ПРАКТИЧЕСКИЕ рекомендации.


(Ответить)(Уровень выше)

alex_executer
2005–12–08 03:32 pm (local) (ссылка) Отслеживать

>1. Все значения полей, которые используются в запросе, дожны быть
>– заключены в кавычки
>– к ним должна быть применена функция mysql_real_escape_string()

Почему именно mysql_real_escape_string()?
Почему не mysql_escape_string()?
Были случаи (способы) взлома? В мане написано, что real только для бинарных данных
Если ты пишешь методику, надо напомнить, что первый пункт недостаточен для защиты от LIKE.
Например:
$q="SELECT id FROM table WHERE name LIKE '".mysql_real_escape_string($_GET['name']).", password LIKE '".mysql_real_escape_string($_GET['password'])."'";


$_GET['name']=$_GET['password']='%';


Понимаю, что такое нормальный человек не пишет, но видел на #php @ irc.rus.net челоека, которому сломали сайт с таким кодом, возможно следует это оговорить.


 
Файлов нет. [Показать файлы/форму]
Комментариев нет. [Показать комментарии/форму]