• Главная
  • Публикации
  • Безопасность/защита информации: автоматизированный механизм защиты сайта от SQL-инъекций и межсайтового скриптинга (PHP)

Безопасность/защита информации: автоматизированный механизм защиты сайта от 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))

{

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;

}

}

Процесс проверки переменных описан в функции fix_val($var, $val). Механизм ее работы предельно прост: она проверяет наличие в тексте значения переменной любого из запрещенных слов с помощью цикла. Для этого массив запрещенных слов сначала определяется как глобальная переменная (он описан в подключаемом файле restricted_words.php), вычисляется его размер, а затем он "проходится" с целью поиска запрещенных слов:

global $restricted_words;

$n = sizeof($restricted_words);

$val = strtoupper(hex2ascii($val));

$j = 0;

while($j < $n)

{

while(!(strpos($val, strtoupper($restricted_words[$j])) === false))

{

# запрещенное слово было найдено

# выполняем "защитные" действия

}

}

Обратите внимание на команду $val = strtoupper(hex2ascii($val));. Во-первых, так как команды для многих атак передаются в шестнадцатеричном виде, необходимо данные символы "вернуть" в кодировку ASCII (иначе теряет смысл попытка описать запрещенные слова в виде массива, так как всевозможных вариантов замены нескольких символов на HEX-код - очень много). Делается это с помощью функции hex2ascii:

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;

}

Во-вторых, не нужно забывать, что, например, SQL не чувствителен к регистру. Поэтому переменная $val переведена в верхний регистр с помощью функции strtoupper. А поиск запрещенных слов ведется также после перевода их в верхний регистр (можно, наоборот, переводить в нижний, это уже дело вкуса).

В случае обнаружения "запрещенного" слова устанавливается флаг признака атаки:

$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]));

$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;

Описанные операции позволяют проводить "замену" в значении переменной "запрещенного" слова на некоторую безопасную специальную подстановку. Возможно, это может оказаться полезным в некоторых случаях. Безопасные подстановки можно определить в файле restricted_words.php, назвав массив $replacement_words. Этот механизм может служить также дополнительным уровнем защиты, когда в протокол (который, внимание, обновляется с помощью SQL-запроса) вносится уже измененное безопасное значение, а для отображения этого протокола производится обратная подстановка.

Полный код модуля secure.php

# информация для подключения к базе данных,

# если планируется протоколирование попыток

# выполнения 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;

}

}

Уважаемые web-разработчики

Вы можете свободно использовать данный модуль безопасности (исходные файлы можно скачать ниже). Убедительная просьба: оставьте в исходных файлах комментарии, указывающие авторство. При перепечатке кодов или фрагментов статьи, пожалуйста, указывайте ссылку на оригинал статьи: http://www.argusm.com/article.php?id=138. В случае возникновения вопросов или комментариев к описанному модулю безопасности свяжитесь с автором, воспользовавшись одним из Контактов.

С анализом результатов эксплуатации описанной системы безопасности можно ознакомиться в статье: Система безопасности geneGuard: теория и практика использования

 

Комментарии

Все комментарии
1комментарий

guest7 27 ноября, 2009 в 13:40:47

'

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

Адаптивное тестирование - быстрая и точная оценка персонала