Вводная
Цель работы
Разобрать основные поля 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) и отправить запрос на получение защищённой информации, продемонстрировать корректный ответ.
- Серверный скрипт для авторизации пользователя
- Серверный скрипт для выдачи данных, доступных после авторизации
- Клиентский скрипт для отправки запроса с перехваченным токеном
Вопросы
- Какие бывают методы в HTTP запросе?
- Назовите примеры заголовков HTTP запроса.
- Какие заголовки явлются обязательными для HTTP запроса?
- Как должны быть настроены заголовки сервера?