пятница, 18 октября 2013 г.

Популярные php-движки - mysql и sql injection.

Поговорим про работу с бд.

Возьмем, современный, красивый и очень позитивный движок shogo cms.

Он использует базу mysql, движок таблиц myisam, все очень типично.

Почему myisam, ну наверно потому что, как гласят городские мифы, он очень очень быстр на чтение. А это очень важно.

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

Если на какой либо странице, нужно будет вывести три раза название категории, это будет три запроса к mysql.

Но не просто запросы, а золотые:

SELECT id FROM _table_ WHERE path LIKE '%[id]%'    

Особый цимес, в том что они сначала проверяют существование записи, а потом извлекают ее.
Как проверяют? Известно как - извлечением.
Т.е два селекта, фул сканом. с лайком, что б получить один id, который они и так знали. Умножим на количество повторений на странице.

Таких запросов на одну страницу, более 70-ти.

Достаточно этого для путевки в ад? Более чем.
Но настоящие герои на этом не остановятся.

Помните про myisam ? Ага, они в эту же таблицу пишут еще какие то даты доступа. А что делает update с myisam? лочит всю таблицу.

В итоге , периодично все висит. Сотни запросов в статусе 
Waiting for table level lock
Но, валят наверняка на хостинг. Купите vds подороже, стандартный ответ в таких случаях.

Просят потом "оптимизируйте настройку сервера, но код не трогайте".

Поговорим про сами запросы, 

что мы подумаем про кодера который пишет такое?

$q = db_query("select description from " . PRODUCTS_OPTIONS_VALUES_VARIANTS_TABLE . " WHERE variantID=".$_GET['param_1']);
Таким людям, прямо  надо обязательно передавать в uri param=1;drop database ...;

Что бы жизнь их хоть чему нибудь научила.

Запомните, все приходящие "снаружи" данные , обязательно должны рассматриваться как зараженные.

И никогда, никогда не надо клеить sql запросы.
Есть же параметры.

Вот образец кода, как делать можно:

$sql= "select id from table where name=?";
$stmt = $mysqli->prepare($sql);
$smt->bind_param('s',$name); //s - type string
$res=$smt->execute;

Но даже если это нужно делать более чем дважды, то лучше вынести это отдельно и вызывать как get_id_.....

Это совсем не панацея, но от примитивный sql injections уже спасет.

Плюсом, если вам нужно вставлять множество значений, субд не придется парсить ваш запрос много раз,  будете дергать prepared запрос, и будет вам счастье.

Откройте ваш любимый php движок, сделайте там grep по prepare, и удивитесь.

Неудивительно что популярные движки такое решето.