Переходы пользователей со стороннего сайта фиксируются в рефевере, получить доступ к которому можно при помощи суперглобального массива $_SERVER['HTTP_REFERER']. Создадим на страница 401.php ссылку на страницу 402.php: <a href = 402.php> Ссылка</a>.
Вывод реферера (страница 402.php)
<?php
echo "Вы перешли на текущую страницу с ". $_SERVER['HTTP_REFERER'];
?>
Вывод реферера (страница 401.php)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Вывод реферера</title>
</head>
<body>
<a href="402.php">Ссылка</a>
</body>
</html>
В окне веб-браузера это будет выглядеть ТАК.
Реферер иногда используется для простейшей защиты HTTP-формы от автопостинга — заполнение HTML-формы при помощи скрипта. Рассмотрим это на примере. Пусть имеется HTML-форма авторизации в файле 403.php.
HTML-форма системы авторизации
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>HTML-форма системы авторизации</title>
</head>
<body>
<table width="200" border="0" >
<form action="404.php" method="post">
<tr>
<td> Имя</td>
<td> <input name="name" type="text" size="25"></td>
</tr>
<tr>
<td> Пароль</td>
<td> <input name="pass" type="password" size="25"></td>
</tr>
<tr>
<td> </td>
<td> <input name="" type="submit" value="Войти"></td>
</tr>
</form>
</table>
</body>
</html>
В окне веб-браузера это будет выглядеть ТАК.
Для защиты HTTP-формы от заполнения на стороннем сайте в обработчик HTTP-формы можно добавить проверку по рефереру.
Защита обработчика HTTP-формы при помощи реферера
<?php
//формируем путь к HTTP-форме на текущем сервере
$html="http://".$_SERVER['SERVER_NAME'];
//проверяем, переданы ли данные с текущего сервера
if(substr($_SERVER['HTTP_REFERER'], 0, strlen($html))==$html)
{
if($_POST['name']=='admin'&&$_POST['pass']=='admin')
{
//обработчик формы
echo "Имя - $_POST[name]<br>";
echo "Пароль - $_POST[pass]<br>";
}
}
else
{
echo "Результат из формы не может быть обработан";
}
?>
Однако такой способ защиты редко используется в реальной практике. Браузер или брандмаузер могут скрывать реферер, поэтому такого рода защита может приводить к некорректной работе веб-приложений для легитимных пользователей. С другой стороны, реферер легко подделывается: достаточно при помощи сокетов отправить HTTP-заголовок Referer или воспользоваться CURL, установив при помощи функции curl_setopt() параметр CURLOPT_REFRRER.
Отправка реферера при помощи сокетов
<?php
$hostname= "localhost";
$path= "/397.php";
$line="";
//Передаем методом POST имя пользователя (admin),
//его пароль (admin), скрытое поле session_id($SID)
//В заголовках передаем cookie PHPSESSID
//Устанавливаем соединение, имя которого
//передано в параметре $hostname
$fp=fsockopen($hostname, 80, $errno, $errstr, 30);
//проверяем успешность установки соединения
if(!$fp) echo "$errstr($errno)<br>/>\n";
else
{
//данные HTTP-запроса
$data="name=admin&pass=admin&\r\n\r\n";
//Заголовок HTTP-запроса
$headers="POST $path HTTP/1.1\r\n";
$headers.= "Host: $hostname\r\n";
$headers.= "Content-type: application/x-www-form-urlencoded\r\n";
$headers.="Content-Length: ".strlen($data)."\r\n";
//Подделываем реферер
$headers.="Referer: http://localhost/406.php\r\n";
$headers.="Connection: Close\r\n\r\n";
//Отправляем HTTP-запрос серверу
fwrite($fp, $headers.$data);
//Получаем ответ
while(!feof($fp))
{
$line.=fgets($fp, 1024);
}
fclose($fp);
}
echo "<pre>";
echo $line ;
echo "</pre>"
?>
В окне веб-браузера это будет выглядеть ТАК.
Подставте в поля ввода любые значения, обработчик все равно передаст значение admin и admin.
Отправка реферера при помощи CURL
<?php
//задаем адрес удаленного севера
$curl=curl_init("http://localhost/397.php");
//Передача данных осуществляется методом POST
curl_setopt($curl, CURLOPT_POST, 1);
//Задаем POST-данные
$data="name=admin&pass=admin&\r\n\r\n";
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
//Устанавливаем реферер
curl_setopt($curl,CURLOPT_REFERER, "HTTP://localhost/408.php");
//Выполняем запрос
curl_exec($curl);
//Закрываем CURL-соединение
curl_close($curl);
?>
В окне веб-браузера это будет выглядеть ТАК.
При обращении РНР-скрипта к страницам сайта при помощи файловых функций, сервер делает в переменную окружения USER_AGENT запись вида: РНР 5.8. В результате скрипт получит доступ к этому идентификатору через элемент суперглобального массива $_SERVER['USER_AGENT']. Однако этот способ проверки не может считаться универсальным, т.к. переменную окружения устанавливает клиент при помощи HTTP-заголовка User-Agent. Любой HTTP-заголовок, который передает клиент, может быть подделан. Рассмотрим пример, в котором скрипт, обращаясь к другому скрипту, выдает себя за пользователя операционной системы Windows XP, использующего браузер Internet Explorer версии 6.0. Для этого в качестве HTTP-заголовка User-Agent передается следующая строка:
Mozila/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Фальсифицируем пользовательский агент
<?php
$hostname= "localhost";
$path= "/410.php"; //файл с HTML-формой
$line="";
//Устанавливаем соединение, имя которого
//передано в параметре $hostname
$fp=fsockopen($hostname, 80, $errno, $errstr, 30);
//проверяем успешность установки соединения
if(!$fp) echo "$errstr($errno)<br>/>\n";
else
{
//Формируем HTTP-заголовки для передачи серверу
$headers="POST $path HTTP/1.1\r\n";
$headers.= "Host: $hostname\r\n";
//Подделываем пользовательский агент, маскируясь
//под пользователя Windows XP
$headers.= "User-Agent: Mozila/4.0"."(compatible; MSIE 6.0; Windows NT 5.1)\r\n";
$headers.="Connection: Close\r\n\r\n";
//Отправляем HTTP-запрос серверу
fwrite($fp, $headers);
//Получаем ответ
while(!feof($fp))
{
$line.=fgets($fp, 1024);
}
fclose($fp);
}
echo "<pre>";
echo $line ;
echo "</pre>"
?>
В окне веб-браузера это будет выглядеть ТАК.
Рассмотрим решение этой задачи с использованием расширения CURL. Для этого при помощи функции curl_setopt() устанавливается параметр CURLOPT_USERAGENT — его значение будет передаваться серверу в HTTP-заголовке User-Agent.
Фальсификация пользовательского агента при помощи CURL
<?php
//задаем адрес удаленного севера
$curl=curl_init("http://localhost/410.php");
//Устанавливаем реферер
$useragent="Mozila/4.0(compatible; MSIE 6.0; Windows NT 5.1)";
curl_setopt($curl, CURLOPT_USERAGENT, $useragent);
//Выполняем запрос
curl_exec($curl);
//Закрываем CURL-соединение
curl_close($curl);
?>
В окне веб-браузера это будет выглядеть ТАК.
Механизм действия cookie заключается в том, что получив указания от сервера, браузер создает в оперативной памяти (если это сессионный cookie) или на жестком диске файл с заданными параметрами. После этого при каждом запросе браузер отправляет серверу HTTP-заголовок Cookie с именем и значением cookie. Так как за установку HTTP-заголовка несет ответственность клиентская сторона, cookie можно легко подделать, отправив HTTP-заголовок следующего формата:
Cookie: name1=value1; name2=value2; . . .
где name1, name2, . . . — имена cookie, а value1, value2, . . . — их значения.
Пусть имеется скрипт, который проверяет наличие cookie, установленного у посетителя при аутентификации. В cookie name устанавливается имя пользователя, а установленные cookie admin, editor, user позволяют провести авторизацию для администратора, редактора и пользователя соответственно.
Предоставление доступа к информации по cookie
<?php
if(isset($_COOKIE['name']))
{
if(isset($_COOKIE['admin']))
{
echo "Здравствуйте, администратор $_COOKIE[name] !<br>";
}
if(isset($_COOKIE['editor']))
{
echo "Здравствуйте, редактор $_COOKIE[name] !<br>";
}
if(isset($_COOKIE['user']))
{
echo "Здравствуйте, пользователь $_COOKIE[name] !<br>";
}
}
?>
Рассмотрим скрипт, осуществляющий аторизацию с правами администратора.
Примечание. Содержимое сессий не может быть фальсифицированно, т.к. хранящиеся в них данные не покидают пределов сервера. Однако фальсификации могут быть подвергнут уникальный идентификатор сессий SID, который как правило, передается через cookie. Таким образом скрипт может вести себя как браузер, поддерживая сессию, или, если злоумышленник получил доступ к идентификатору действующей сессии (например, в результате XSS-атаки), получать доступ к данным другого пользователя.
Подделка cookie
<?php
$hostname= "localhost";
$path= "/412.php";
//Устанавливаем соединение, имя которого
//передано в параметре $hostname
$fp=fsockopen($hostname, 80, $errno, $errstr, 30);
//проверяем успешность установки соединения
if(!$fp) echo "$errstr($errno)<br>/>\n";
else
{
//Формируем HTTP-заголовки для передачи серверу
$headers="POST $path HTTP/1.1\r\n";
$headers.= "Host: $hostname\r\n";
//Подделываем cookie
$headers.="Cookie: name=shut; admin=1; \r\n";
$headers.="Connection: Close\r\n\r\n";
//Отправляем HTTP-запрос серверу
fwrite($fp, $headers);
//Получаем ответ
while(!feof($fp))
{
$line.=fgets($fp, 1024);
}
fclose($fp);
}
echo "<pre>";
echo $line ;
echo "</pre>"
?>
В окне веб-браузера это будет выглядеть ТАК.
Для того, чтобы подделать cookie, скрипт отправляет HTTP-заголовок:
Cookie: name=shut; admin=1; \r\n
в котором передается имя пользователя shut и cookie admin со значение 1. В результате работы скрипта HTTP-заголовки ответа, которые предваряют вывод, не фильтруются, но могут быть легко удалены в случае необходимости.
Не составляет труда авторизоваться также с правами редактора или пользователя: достаточно заменить имя cookie admin на editor или user. Более того, в условиях скрипта можно произвести авторизацию одновременно всех трех видов пользователей. Для этого достаточно отправить HTTP-заголовок вида:
Cookie: name=shut; admin=1; editor=1; user=1; \r\n
В окне веб-браузера это будет выглядеть ТАК.
Все хосты в сети Интернет имеют уникальные IP-адреса, однако для удобства вместо них используются доменные имена, которые при помощи DNS-серверов привязываются к IP-адресам. В протоколе HTTP1.1 появилась возможность привязать к одному IP-адресу несколько доменных имен.
Помимо функции fsockopen(), РНР располагает несколькими функциями, предназначенными для работы с IP-адресами и доменными именами. Кроме того при помощи сокетов можно обратиться к Whois-серверу, который предоставляет информацию об IP-адресах.
Функции для работы с IP-адресами и доменными именами
Функция | Описание |
checkdnsrr($hostname [, $type]) | Находит на DNS-сервере записи ресурсов вида $type для хоста $hostname |
gethostbyname($hostname) | Возвращает для доменного имени $hostname соответствующий ему IP-адрес |
gethostbynamel($hostname) | Возвращает для доменного имени $hostname соответствующие ему IP-адреса |
gethostbyaddr($ip_address) | Для IP-адреса $ip_address возвращает соответствующее ему доменное имя |
ip2long($ip_address) | Преобразует IP-адрес $ip_address в целое число |
long2ip($proper_address) | Преобразует целое число в IP-адрес $proper_address |
Функция gethostbyname() принимает в качестве аргумента строку, представляющую доменное имя компьютера, и возвращает соответствующий IP-адрес. Функция имеет следующий синтаксис:
string gethostbyname($hostname)
Рассмотрим пример получения IP-адреса сервера сайта http://sevidi.narod.ru.
Использование функции gethostbyname ()
<?php
$hostname="http://sevidi.narod.ru";
$ip_address=gethostbyname($hostname);
echo "IP-адрес $hostname: $ip_address";
?>
В окне веб-браузера это будет выглядеть ТАК.
Если компьютер подключен к сети Интернет, результатом работы скрипта из листинга может быть строка:
IP-адрес http://sevidi.narod.ru: 213.180.199.61
Многие компьютеры имеют несколько IP-адресов. Особенно типична такая ситуация для серверов. Получить полный список IP-адресов, соответствующему данному имени компьютера, можно с помощью функции gethostbynamel(), дейстиующей аналогично функции gethostbyname().
Функция имеет следующий синтаксис:
string gethostbynamel($hostname)
Другая ситуация, в которой полезно применение этой функции — если одно имя DNS соответствует нескольким компьютерам. Это случается при работе с DNS-серверами, поддерживающими механизм кругового распределения нагрузки, при котором одно имя DNS-сервера отображается на несколько компьютеров в локальной сети этого сервера.
Возвращаемый список IP-адресов функция gethostbynamel() помещает в массив.
Использование функции gethostbynamel()
<?php
$hostname="http://sevidi.narod.ru";
$ip_address=gethostbynamel($hostname);
echo "<pre>";
print_r($ip_address);
echo "</pre>";
?>
В окне веб-браузера это будет выглядеть ТАК.
Если компьютер подключен к сети Интернет, результатом работы скрипта может быть следующий дамп:
Array ( [0] => 213.180.199.61 )
Функция gethostbyaddr() принимает в качестве аргумента IP-адрес $ip_address и возвращает соответствующее ему имя хоста. Функция имеет следующий синтаксис:
string gethostbyaddr($ip_address)
Рассмотрим пример использования функции gethostbyaddr() применительно к локальному хосту.
Использование функции gethostbyaddr()
<?php
$ip_address="127.0.0.1";
$hostname=gethostbyaddr($ip_address);
echo "Имя хоста с IP-адресом $ip_address: $hostname";
?>
В окне веб-браузера это будет выглядеть ТАК.
Сервис Whois позволяет определить, на кого зарегистрирован тот или иной IP-адрес. Для этого достаточно послать IP-адрес по порту 43 соответствующего Whois-сервера.
Примечание. Сервис Whois является официальным сервисом центра RIPE, предоставляющего информацию о зарегистрированных на данный момент доменных именах, о DNS-серверах, поддерживающих IP-адреса, и о владельцах IP-адресов. Когда мы на различных сайтах проверяем, существует ли на данный момент какое-либо доменное имя, то пользуемся именно сервисом Whois. RIPE(Reseaux IP Europeens) —это Европейская континентальная сеть TCP/IP. Первоначально все IP-адреса, которые мы используем в RuNet, получают в RIPE Network Coordination (Координационный центр RIPE), который является одним из трех региональных интернет-регистраторов, поддерживающих глобальную инфраструктуру Интернета. Сеть RIPE управляется сетью Eunet. Последняя представляет собой сеть UNIX для Европы и является старейшим и крупнейшим поставщиком услуг Интернета в Европе, частью которого является российское АО "РЕЛКОМ", являющееся крупнейшим поставщиком услуг Интернета в России.
Существует множество Whois-серверов, каждый из которых несет ответственность за IP-адреса той или иной части света. Главным сервером является whois.arin.net. Если главный Whois-сервис не содержит информации о запрашиваемом IP-адресе, то в отчете обязательно присутствует строка ReferralServer, которая указывает адрес Whois-сервиса, ответственного за нужный диапазон IP-адресов. Поэтому обращение к Whois-сервису удобно выделить в отдельную функцию whois(), которая будет принимать два параметра: адрес Whois-сервиса и запрашиваемый IP-адрес. Если полученный отчет будет содержать ссылку на реферальный сервер, функция будет осуществлять рекурсивный спуск.
Создание Whois-сервиса
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Рекурсивное следование реферальному серверу</title>
</head>
<body>
<form action="418.php" method="post">
<input name="ip" type="text" size="35">
<br>
<input name="" type="submit" value="Введите IP-адрес">
</form>
<?php
$url="whois.arin.net";
if(!empty($_POST['ip'])) echo whois($url, $_POST['ip']);
function whois($url, $ip)
{
//Соединение с сокетом ТСР, ожидающим на сервере
//whois.arin.net по порту 43
//В результате возвращается дескриптор соединения $sock
$sock=fsockopen($url, 43, $errno, $errstr);
if(!$sock) exit("$errno($errstr)");
else
{
echo $url."<br>";
//Записываем строку из переменной $_POST['ip']
//в дескриптор сокета
fputs($sock, $ip."\r\n");
//Осуществляем чтение из дескриптора сокета
$text="";
while(!feof($sock))
{
$text.=fgets($sock, 128)."<br>";
}
//Закрываем соединение
fclose($sock);
//Ищем реферальный сервер
$pattern="|ReferalServer: whois://([^\n<:]+)|i";
preg_match($pattern, $text, $out);
if(!empty($out[1])) return whois($out[1], $ip);
else return $text;
}
}
?>
</body>
</html>
В окне веб-браузера это будет выглядеть ТАК.
Принадлежность IP-адреса 192.168.0.0.1
Скрипт всегда начинает поиск с whois.arin.net, при необходимости осуществляет повторные запросы к реферальным сервисам. При этом перед отчетом выводится список Whois-сервисов, которые посещает скрипт. Если Whois-сервер, к которому происходит обращение, не может предоставить информацию по данному IP-адресу, то он в своем отрицательном ответе указывает, какой именно Whois-сервер ответственен за диапазон IP-адресов, к которому принадлежит запрашиваемый IP-адрес.
Отрицательный ответ Whois-сервиса
Обращать внимание при анализе отрицательного ответа Whois-сервиса надо на строку
ReferralServer: whois://whois.apnic.net
В этой строке указан справочный сервер, к которому необходимо обращаться для того, чтобы получить информацию о нужном IP-адресе.
Региональные доменные имена третьего уровня, как правило, обслуживают региональные Whois-сервисы. К примеру, доменные имена вида spb.ru, msk.ru находятся на обслуживании ООО "Релком. ДС", и получить информацию о них можно, обратившись к Whois-сервису whios.relcom.ru.