Безопасность/защита информации: автоматизированный механизм защиты сайта от SQL-инъекций и межсайтового скриптинга (PHP)
Ключевые слова: программирование, PHP, web, безопасность, защита информации, защита от взлома, sql-инъекция, межсайтовый скриптинг (programming, PHP, web, security, attack, information security, sql-injection, cross-site scripting)
Автор: Приходько Максим Александрович
Практический любой web-разработчик в тот или иной момент своей работы сталкивался с проблемой защиты своего сайта от возможных хакерских атак. Наиболее распространены сейчас два вида атак на сайты: SQL-инъекция и межсайтовый скриптинг. О каждом из них написано немало статей, поэтому ограничимся буквально парой слов.
В случае SQL-инъекции в переменную, которая используется в одном из запросов, передается специально сконструированное значение, которое направлено на "подмену" выполняющегося sql-запроса. Делается это, например, с целью получения хэш-кода пароля пользователя (в первую очередь администратора).
Межсайтовый скриптинг в свою очередь направлен на выполнение "от лица" зарегистрированного пользователя тех или иных скриптов. Делается это в основном для получения cookies, которые затем используются для "перехвата" сессии работы с сайтом.
Сколько было прочитано статей, столько же было дано предложений по борьбе с атаками описанных видов. В общем случае все советы сводились к одному - аккуратно и тщательно проверяйте переменные, используемые в формах, а также внимательно конструируйте sql-запросы, "защищенные" от sql-инъекции.
Удивительно, но факт. Дальше подобных советов дело ни в одной статье не шло. Могу предположить, что мне просто не посчастливилось найти статью с решением, к которому в итоге пришел самостоятельно.
Решение, как это обычно бывает, лежало на поверхности. Достаточно действительно просто проверять переменные, только делать это надо не в каждом конкретном случае, а в самом начале формирования PHP-страницы, причем автоматизировав процесс проверки.
Для защиты вашего сайта потребуется создать дополнительный файл, например, secure.php, который будет подключаться в самом начале каждой страницы с помощью команды
include ("secure.php");Также понадобится создать еще один файл, например restricted_words.php, в котором будет описан массив "запрещенных" слов, примерно следующего содержания:
$restricted_words = array();Данный файл необходимо подкючить в файле, обеспечивающем безопасность:$restricted_words[0] = "select";
$restricted_words[1] = "insert";
$restricted_words[2] = "update";
$restricted_words[3] = "delete";
$restricted_words[4] = "order by";
$restricted_words[5] = "where";
$restricted_words[6] = "from";
$restricted_words[7] = "union";
$restricted_words[8] = "http";
$restricted_words[9] = "script";
$restricted_words[10] = "www";
$restricted_words[11] = "cookie";
$restricted_words[12] = "document.location";
$restricted_words[13] = "java";
include ("restricted_words.php");Непосредственная проверка переменных, передаваемых любым из методов GET или POST, выглядит следующим образом:
if(isset($HTTP_GET_VARS))Процесс проверки переменных описан в функции fix_val($var, $val). Механизм ее работы предельно прост: она проверяет наличие в тексте значения переменной любого из запрещенных слов с помощью цикла. Для этого массив запрещенных слов сначала определяется как глобальная переменная (он описан в подключаемом файле restricted_words.php), вычисляется его размер, а затем он "проходится" с целью поиска запрещенных слов:{
while(list($var,$val)=each($HTTP_GET_VARS))
{
$val = fix_val($var, $val);
$$var=$val;
}
}
if(isset($HTTP_POST_VARS))
{
while(list($var,$val)=each($HTTP_POST_VARS))
{
$val = fix_val($var, $val);
$$var=$val;
}
}
global $restricted_words;Обратите внимание на команду $val = strtoupper(hex2ascii($val));. Во-первых, так как команды для многих атак передаются в шестнадцатеричном виде, необходимо данные символы "вернуть" в кодировку ASCII (иначе теряет смысл попытка описать запрещенные слова в виде массива, так как всевозможных вариантов замены нескольких символов на HEX-код - очень много). Делается это с помощью функции hex2ascii:
$n = sizeof($restricted_words);
$val = strtoupper(hex2ascii($val));
$j = 0;
while($j < $n)
{
while(!(strpos($val, strtoupper($restricted_words[$j])) === false))
{
# запрещенное слово было найдено
# выполняем "защитные" действия
}
}
function hex2ascii($str)Во-вторых, не нужно забывать, что, например, SQL не чувствителен к регистру. Поэтому переменная $val переведена в верхний регистр с помощью функции strtoupper. А поиск запрещенных слов ведется также после перевода их в верхний регистр (можно, наоборот, переводить в нижний, это уже дело вкуса).{
while(!(strpos($str, "%") === false))
{
$pos = strpos($str, "%");
$len = strlen($str);
$tmp = substr($str, 0, $pos).chr(hexdec(substr($str, $pos + 1, 2)));
if($pos + 3 < $len)
$tmp = $tmp.substr($str, $pos + 3, $len - $pos + 3 + 1);
$str = $tmp;
}
return $str;
}
В случае обнаружения "запрещенного" слова устанавливается флаг признака атаки:
$attacked = 1;В конце в зависимости от значения флага действия варьируются. Если атака не была обнаружена, возвращается оригинальное значение переменной. Если атака была обнаружена, переменная аннулируется:
if($attacked == 0) return $x_val;
if($attacked == 1)
{
# команды записи протокола атаки в базу данных,
# если ведется протоколирование атак
return "";
}
Полный код функции fix_val($var, $val) выглядит следующим образом:
function fix_val($var, $val)Обратите внимание на последний фрагмент кода:{
global $restricted_words;
$attacked = 0;
$n = sizeof($restricted_words);
$o_val = addslashes($val);
$x_val = $val;
$val = strtoupper(hex2ascii($val));
$j = 0;
while($j < $n)
{
while(!(strpos($val, strtoupper($restricted_words[$j])) === false))
{
$attacked = 1;
$pos = strpos($val, strtoupper($restricted_words[$j]));
$len = strlen($val);
$tmp = substr($val, 0, $pos).strtoupper($replacement_words[$j]);
if($pos + strlen($restricted_words[$j]) < $len)
$tmp = $tmp.substr($val, $pos + strlen($restricted_words[$j]), $len - $pos + strlen($restricted_words[$j]) + 1);
$val = $tmp;
}
$j++;
}
if($attacked == 0) return $x_val;
if($attacked == 1)
{
# команды записи протокола атаки в базу данных,
# если ведется протоколирование атак
return "";
}
}
$pos = strpos($val, strtoupper($restricted_words[$j]));Описанные операции позволяют проводить "замену" в значении переменной "запрещенного" слова на некоторую безопасную специальную подстановку. Возможно, это может оказаться полезным в некоторых случаях. Безопасные подстановки можно определить в файле restricted_words.php, назвав массив $replacement_words. Этот механизм может служить также дополнительным уровнем защиты, когда в протокол (который, внимание, обновляется с помощью SQL-запроса) вносится уже измененное безопасное значение, а для отображения этого протокола производится обратная подстановка.$len = strlen($val);
$tmp = substr($val, 0, $pos).strtoupper($replacement_words[$j]);
if($pos + strlen($restricted_words[$j]) < $len)
$tmp = $tmp.substr($val, $pos + strlen($restricted_words[$j]), $len - $pos + strlen($restricted_words[$j]) + 1);
$val = $tmp;
# информация для подключения к базе данных,# если планируется протоколирование попыток
# выполнения SQL-инъекций и межсайтового
# скриптинга
# подключение библиотек с необходимыми функциями
include("restricted_words.php");
# определение идентификатора работающего
# пользователя и его ip-адреса для записи
# в базу данных в случае протоколирования
# атак
function hex2ascii($str)
{
while(!(strpos($str, "%") === false))
{
$pos = strpos($str, "%");
$len = strlen($str);
$tmp = substr($str, 0, $pos).chr(hexdec(substr($str, $pos + 1, 2)));
if($pos + 3 < $len)
$tmp = $tmp.substr($str, $pos + 3, $len - $pos + 3 + 1);
$str = $tmp;
}
return $str;
}
function fix_val($var, $val)
{
global $restricted_words;
$attacked = 0;
$n = sizeof($restricted_words);
$o_val = addslashes($val);
$x_val = $val;
$val = strtoupper(hex2ascii($val));
$j = 0;
while($j < $n)
{
while(!(strpos($val, strtoupper($restricted_words[$j])) === false))
{
$attacked = 1;
$pos = strpos($val, strtoupper($restricted_words[$j]));
$len = strlen($val);
$tmp = substr($val, 0, $pos).strtoupper($replacement_words[$j]);
if($pos + strlen($restricted_words[$j]) < $len)
$tmp = $tmp.substr($val, $pos + strlen($restricted_words[$j]), $len - $pos + strlen($restricted_words[$j]) + 1);
$val = $tmp;
}
$j++;
}
if($attacked == 0) return $x_val;
if($attacked == 1)
{
# команды записи протокола атаки в базу данных,
# если ведется протоколирование атак
return "";
}
}
if(isset($HTTP_GET_VARS))
{
while(list($var,$val)=each($HTTP_GET_VARS))
{
$val = fix_val($var, $val);
$$var=$val;
}
}
if(isset($HTTP_POST_VARS))
{
while(list($var,$val)=each($HTTP_POST_VARS))
{
$val = fix_val($var, $val);
$$var=$val;
}
}
Вы можете свободно использовать данный модуль безопасности (исходные файлы можно скачать ниже). Убедительная просьба: оставьте в исходных файлах комментарии, указывающие авторство. При перепечатке кодов или фрагментов статьи, пожалуйста, указывайте ссылку на оригинал статьи: http://www.argusm.com/article.php?id=138. В случае возникновения вопросов или комментариев к описанному модулю безопасности свяжитесь с автором, воспользовавшись одним из Контактов.
С анализом результатов эксплуатации описанной системы безопасности можно ознакомиться в статье: Система безопасности geneGuard: теория и практика использования
guest7 27 ноября, 2009 в 13:40:47
'
Добавить комментарий