Вводная

Цель работы

Разобрать основные поля HTTP запроса, средства перехвата и отправки подделанного запроса.

Введение

HTTP расшифровывается как HyperText Transfer Protocol (протокол передачи гипертекста). Протокол — это набор правил, по которым разные устройства обмениваются данными. Всё, что вы видите в данном окне браузера, было получено посредством этого протокола.

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

GET /other-19 HTTP/1.1
Host: www.scriptsite.ru
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5
(.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
                

При этом первая строка является строкой запроса, а все остальные строки — это http-заголовки, несущие в себе дополнительную информацию о запросе, о клиенте, который эту информацию запрашивает, и о многих других вещах.

В ответ на наш запрос сервер может отослать, например, такой ответ:

HTTP/1.x 200 OK
Date: Sat, 12 Dec 2009 15:41:52 GMT
Server: Apache/2.0.61 (Unix) mod_ssl/2.0.61 OpenSSL/0.9.8k mod_dp20/0.99.2 PHP/5.2.5
mod_python/3.3.1 Python/2.5.1 mod_ruby/1.2.6 Ruby/1.8.6(2007-09-24)
X-Powered-By: PHP/5.2.5
Set-Cookie: PHPSESSID=ft47gokfee6amv3eda3k1p93s3; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Keep-Alive: timeout=10, max=1024
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html
    

Первая строка — строка статуса. Остальные строки это заголовки.

Для того, чтобы увидеть http-заголовки, обычно достаточно воспользоваться встроенными в браузер инстурментами разработчика. Если же хочется максимально удобной отладки, то лучше воспользоваться HTTP сниффером - например, Fiddler.

Приводить полный список заголовков смысла нет - из названия довольно информативны, и всегда можно посмотреть их в той же википедии.

Давайте представим, что нам надо получить страницу с сайта, передав Cookie, и более того, нам известно, что на данную страницу пускают только после того, как Вы побывали на главной странице сайта.

Напишем наш запрос.

GET http://www.site.ru/news.html HTTP/1.0\r\n
Host: www.site.ru\r\n
Referer: http://www.site.ru/index.html\r\n
Cookie: income=1\r\n
\r\n

Данный запрос говорит нам о том, что мы хотим получить содержимое страницы по адресу http://www.site.ru/news.html, использую метод GET. Поле Host говорит о том, что данная страница находится на сервере www.site.ru, поле Referer говорит о том, что за новостями мы пришли с главной страницы сайта, а поле Cookie говорит о том, что нам была присвоено некое значение куки в переменной income равное 1. Почему так важны поля Host, Referer и Сookie? Потому что очень часто программисты при создании динамических сайтов проверяют данные поля, которые появляются в скриптах в виде переменных - для того, чтобы сайт не могли автоматически скачать, чтобы попасть на внутренние страницы можно было с главной, и так далее.

Другой пример. Давайте представим, что нам нужно отправить некий запрос таким образом, словно пользователь зполнил и отправил некую форму на веб страничке. Пусть в данной форме будет два поля: login и password, и мы знаем логин и пароль.

GET http://www.site.ru/news.html?login=Petya%20Vasechkin&password=qq HTTP/1.0\r\n
Host: www.site.ru\r\n
Referer: http://www.site.ru/index.html\r\n
Cookie: income=1\r\n
\r\n

Логин у нас "Petya Vasechkin". Почему же мы должны писать "Petya%20Vasechkin"? Это из-за того, что специальные символы могут быть распознаны сервером, как признаки наличия нового параметра, конца запроса и так далее. Поэтому существует алгоритм кодирования имен параметров и их значений, во избежание ошибочных ситуаций в запросе. В PHP для данной цели есть функции rawurlencode и rawurldecode для кодирования и декодирования соответственно. Следует отметить, что, если в запросе были переданы закодированные параметры, то декодирование РНР делает сам.

Теперь мы знаем, что должно быть в теле HTTP запроса. Однако, как его отправить? В случае, если мы работаем с PHP, можно воспользоваться встроенными функциями для работы с сокетами. Пример ниже иллюстрирует отправку формы методом POST и вывод результатов:

<?
$xml='Test';
$posts='xml='.$xml;
$query="POST /test.php HTTP/1.0\r\n".
    "Accept: text/html, application/xml;q=0.9, application/xhtml+xml\r\n".
    "Accept-Charset: utf-8\r\n".
    "Content-Type: application/x-www-form-urlencoded\r\n".
    "Content-Length: ".strlen($posts)."\r\n\r\n".$posts."\r\n\r\n";
$socket=fsockopen('localhost',80,$errno,$errstr,60);
fwrite($socket,$query);
while(!feof($socket)){
    $s.=fgets($socket);
}
fclose($socket);
echo $s;
?>

Естественно, можно использовать любые другие средства для генерации и отправки HTTP запросов - браузеры, плагины для браузеров, снифферы, консольные утилиты (например, curl), или даже сделать это на bash:

exec 3<> /dev/tcp/example.com/80

DATA='{"email": "foo@example.com"}'
LEN=$(printf "$DATA" | wc -c)

cat >&3 << EOF
POST /api/retrieveInfo HTTP/1.1
Host: example.com
User-Agent: Bash
Accept: */*
Content-Type:application/json
Content-Length: $LEN
Connection: close

$DATA
EOF

# Read response.
while read line <&3; do
   echo $line
done

Дополнительные материалы

Задание

Внимание! Примеры создаются и эксплуатируются только на ваших тестовых проектах. Запрещается их демонстрация на каких либо внешних сайтах.
  • Написать серверную часть, реализующую авторизацию по логину и паролю, и защищающую некие данные при помощи access token, который записывается в cookie
  • Реализовать "перехват" токена при помощи сниффера
  • Сгенерировать средствами языка разработки (например, PHP) и отправить запрос на получение защищённой информации, продемонстрировать корректный ответ.
В результате у вас должно получиться 3 PHP скрипта:
  • Серверный скрипт для авторизации пользователя
  • Серверный скрипт для выдачи данных, доступных после авторизации
  • Клиентский скрипт для отправки запроса с перехваченным токеном

Вопросы

  1. Какие бывают методы в HTTP запросе?
  2. Назовите примеры заголовков HTTP запроса.
  3. Какие заголовки явлются обязательными для HTTP запроса?
  4. Как должны быть настроены заголовки сервера?