Заголовки HTTP и фильтрация данных в PHP


Отправка HTTP заголовков средствами PHP

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

Header XSS injection

В задачи которого входит держать удар определение и запись ботов IP адресов атакующих хостов в соответствующие логи. Всю остальную работу делает (файрвол) iptables.

Но это так, к слову.

На днях мне необходимо было протестировать работу одного серверного скрипта, который отвечает за аутентификацию доступа к закрытым директориям сайта по ip адресу.

Понятное дело, что использование htaccess для ограничения доступа по ip в данном случае просто не приемлемо, ибо htaccess воспринимает только REMOTE_ADDR, которая формируется на низком уровне, а т. к. последний обмен рукопожатиями у веб сервера происходит с проксиком, то и REMOTE_ADDR всегда один и тот же.

Для тестирования я быстренько набросал PHP скрипт, который условно можно обозвать сокетный анонимайзер. В голом виде он выглядит так:

<?php function get_page($host, $path)
{
$count = "";
$opens = fsockopen($host, 80, $errno, $errstr, 30);
if (!$opens) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET $path HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "X-Forwarded-For: 20.20.20.20\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($opens, $out);
while (!feof($opens))
{
$count .= fgets($opens, 1024);
}
fclose($opens);
}
return $count;
}
$host = "testsite.com";
$path = "/closedir";
set_time_limit(150);
echo get_page($host, $path);
?>

В зависимости от настроек, прокси сервер может передавать заголовок X-Forwarded-For, в который пишет клиентский ip адрес.

Собственно данный скрипт и имитирует его работу.

Через некоторое время мое тестирование закончилось, и я тут же поймал себя на мысли:

Все те сайты, которые в режиме онлайн определяют и отображают юзерагента, а так же все остальное, что может передать пользователь в HTTP заголовках, фильтруют ли они входящие данные, которые так легко подделать?

Примерно минут через 15 я сам ответил на свой вопрос:

В среднем 5 из 10 онлайн «определителей» слепо верят тому, что им отправляют честные пользователи. 😀

xss injection header

Думаю не малая опасность заключается в том, что HTTP заголовки обрабатываются в серверной части веб приложения, а банальное:

$a= "весело очень в обезьяннике ночью";
$out .= "User-Agent:$a\r\n";

Дает достаточно предсказуемый результат.

header php injection

На некоторых сайтах, вызов несуществующих страниц, показывал всю картину (что, где, когда) возникновения ошибки.

PHP анонимайзер

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

Как вы поняли, пользователь (клиент) может воздействовать не только на явные параметры передаваемые веб приложению (отправка сообщений, изменение GET запросов в урле и т. д.)  но и на передаваемые заголовки (User-Agent, Reffer, Client-ip, X-Forwarded-For и др.)

Причем, некоторые используют $_SERVER['HTTP_X_FORWARDED_FOR'] (для точного определения ip) что в корне является неправильным, исключение может составлять структура, которая описывалась мною в начале статьи. Т. к. проксик в передаваемый заголовок X-FORWARDED-FOR запишет результат $_SERVER['REMOTE_ADDR'], а он, повторюсь, формируется на низком уровне.

Что и как фильтровать в PHP

Вопрос конечно, больше индивидуальный, чем риторический, однако, базовые рекомендации есть:

1. Использование функции strip_tags(), которая удалит все NULL-байты, HTML, PHP теги, и вернет уже отформатированную строку.

2. Функция htmlspecialchars() преобразует все спец. символы в HTML-сущности.

Однако оптимальная защита, на мой взгляд, должна строится по принципу:

Все, что не разрешено — запрещено.

3. Использование регулярных выражений PHP и Perl.

Если нужно отобразить на странице содержание заголовка $_SERVER['HTTP_X_FORWARDED_FOR'] то почему бы и не написать  небольшую функцию, которая произведет проверку и точное форматирование получаемого значения из заголовка HTTP_X_FORWARDED_FOR.

<?php
//смотрим, что там у нас в HTTP заголовке X-Forwarded-For
$ip = getenv("HTTP_X_FORWARDED_FOR");
function cut($var)
{
$var=preg_replace("/[^0-9.\n]/i","", $var);
$var = substr($var, 0, 15);
return
$var;
}
//некорректный вывод
echo "Содержание заголовка X-Forwarded-For:  " . $ip;
//подключаем функцию cut(), и делаем "правильные" выводы
echo " Содержание заголовка  X-Forwarded-For: " . cut($ip);
?>

После подключения функции в переменной $ip будут только цыферки и точки, а длинна строки не будет превышать 15 символов. Тут делов то, на 5 минут.  😉