Дата: 25.08.2008, Категория: Кодинг Написал этот пост для блогера с ником DimoninG, так как отвечать на один из его вопросов в комментариях было не удобно.
DimoninG пишет руководство по созданию собственного блога на php. Он считает, что возможно защититься от атак sql-injection, просто используя mod_rewrite, чем вводит многих в заблуждение.
Цитата:
Если хакер попытается ввести в адрес не буквенно-цифровой символ, то сервер прервет запрос сразу же. То есть заботиться об этом в самом движке уже не надо.
Просто в данном случае мне кажется, что при записи в .htaccess вида A-Za-z0-9_ нельзя подставить никакую sql-inj.
Приведите ее пример, пожалуйста, который бы срабатывал и при этом хакал или хотя бы вешал двигло?.. Кроме подставления всякого бреда типа ’sdfsdfwerekjhf’, запись о котором просто не найдется в базе данных. Ну и без всякого бреда, типа переполнение буфера интерпретатора, я не знаю :)
Даже кавычку не подставить - сразу вылезет ошибка веб-сервера.
В какой-то степени, это правда. Но! Это ГРУБЕЙШАЯ ошибка программистов. Взломщик может подобрать запрос вида index.php?category=[sql-inj] (к примеру), тут никакой mod_rewrite не поможет.
НИКОГДА нельзя доверять данным, получаемым со стороны клиента! Ибо их можно подделать. Что ж, будем действовать "от противного", то есть напишем заведомо небезопасасный код, в котором не будем фильтровать входящие данные.
Просили пример? Получайте.
Рассмотрим часть кода, отвечающую за выборку категорий для блога. Итак, создаем 2 файла index.php и .htaccess. В .htaccess прописываем следующее правило для mod_rewrite:
RewriteEngine on
RewriteRule ^(category)/([A-Za-z0-9_]+)/$ index.php?category=$2 [L]
Теперь к категориям можно будет обращаться так: http://site/category/category_1/.
Создаем бд 'blog' и прописываем туда две таблички - categories (здесь будут храниться категории), и admin (допустим, здесь у нас будет храниться информация о доступе админа).
CREATE TABLE `categories` (
`id` int(3) NOT NULL auto_increment,
`url` varchar(255) NOT NULL,
`title` varchar(255) NOT NULL,
PRIMARY KEY(`id`)
);
INSERT INTO `categories` VALUES (1, 'category_1', 'Первая категория');
INSERT INTO `categories` VALUES (2, 'category_2', 'Вторая категория');
INSERT INTO `categories` VALUES (3, 'category_3', 'Третья категория');
CREATE TABLE `admin` (
`id` int(3) NOT NULL auto_increment,
`login` varchar(32) NOT NULL,
`password` varchar(32) NOT NULL,
PRIMARY KEY(`id`)
);
INSERT INTO `admin` VALUES (1, 'blog_admin', '1a1dc91c907325c69271ddf0c944bc72');
Пишем код, отвечающий за показ категорий. index.php:
<?php
// подключаемся к бд
$db = mysql_connect('localhost','root','password') or die();
mysql_select_db('blog',$db) or die();
/*
Пропускаем часть кода. Оставляем случай с выборкой категорий
*/
// определяем, какую категорию показывать
if(isset($_GET['category'])) {
$category = $_GET['category']; // никаких проверок не делаем
// формируем запрос на выборку названия категории
$q = mysql_query("SELECT title FROM categories WHERE url='$category' LIMIT 0,1");
if(mysql_num_rows($q) != 0) {
$r = mysql_fetch_object($q);
echo 'Текущая категория: '.$r->title; // выводим категорию
} else { echo 'Категория отсутсвтует'; }
} else {
// выводиим все категории
$q = mysql_query('SELECT url,title FROM categories ORDER BY id DESC');
if(mysql_num_rows($q) != 0) {
while($r = mysql_fetch_assoc($q)) {
echo "<a href='/category/".$r['url']."/'>".$r['title']."</a><br>";
}
} else { echo 'Категорий нет'; }
}
?> |
Код прокомментирован, так что объяснять что да как, не буду. Как вы уже заметили, мы получаем значение $_GET['category'], совершенно не проверяя его. В результате получаем потенциальную уязвимость sql-injection.
Расскажу подробнее. Как и говорил товарищ DimoninG, при следующем обращении к категории - http://site/category/category_1/ - мы получаем строку "Текущая категория: Первая категория", тут все нормально. Теперь попробуем подставить кавычку вместо "category_1" - как и ожидалось, мы получаем 404 ошибку apache, то есть мы не можем произвести атаку.
А теперь внимаение... Что мешает злоумышленнику подобрать запрос вида http://site/index.php?category=' ? Правильно, ничего. Результом будет ошибка MySQL. Далее, злоумышленник будет пытаться произвести атаку, подбирая запрос к бд.
/index.php?category=test'+or+1=1/* - получим "Текущая категория: Первая категория"
/index.php?category=test'+union+select+1/* - получим "1"
/index.php?category=test'+union+select+concat(login,0x3a,password)+from+admin+limit+0,1/* - В итоге подобрали примерно такой запрос, который выведет нам логин и md5 хэш пароля админа (admin:1a1dc91c907325c69271ddf0c944bc72).
Защититься можно, проверяя входящие данные, в нашем случае $_GET['category']. Например, так:
if(eregi("[^a-z0-9_-]",$_GET['category'])) { // продолжаем работать } else { die(); }
Рекомендуется почитать побольше про sql-injection, да и про то, как защищать скрипты, тоже следовало бы почитать, ибо такие ошибки, не редко, приводят к очень плачевным последствиям.
DimoninG 25.08.2008 Мой ник пишется не так, уважайте собеседника, что ли.
Мой ответ: http://dimoning.ru/kak-napisat-svoy-dvizhok-bloga-1.html#comment-1298
c0nst 25.08.2008 DimoninG, извини, ошибся с ником. Уже поправил.
Corwin 07.09.2008 странно что кодер этого не знал. могу подтвердить - в большинстве случаев запросы подбираются крайне легко, а если и нет, то по возможности скачиваем сорцы движка и все.
Сыроедение, голодание, здоровый 20.10.2008 Статья очень полезная. +1 аффтару. Пиши исчё
|
|
О блоге
- Освещение проблем безопасности web приложений и серверов. Поисковая оптимизация.
Ник: c0nst
icq: 331699888
Рубрики
RSS-лента
Друзья и партнеры
Счетчики
|
|