Saturday, February 20, 2010

Авторизатор для моего ISP (Обработка HTML в Python)

Предыстория.
После весьма болезненного перехода на нового провайдера Интернет, появилась задача написать авторизатор. Дело в том что провайдер дает возможность доступа к сети двумя способами:
  1. с помощью специальной программки, которая написана для ОС Windows. Програмка совсем неплохая и даже отлично работает под Wine (разве что криво русские шрифты отображаются).
  2. с помощью авторизаци через web browser. Происходит это примерно так: пользователь заходит на сайт провайдера (который всегда доступен), вводит в нужные формы логин и пароль. В последствии загружается страница, что обновляется через каждые 40 сек. Конечно же если мы закроем страницу, то по истечении 40 сек. доступ в Интернет оборвется.
Решил попробовать свои силы в написании скрипта авторизации, использовать попробую Python. Я совсем не эксперт в этой области и далеко от уверенного пользователя, потому наличие не оптимально написанного кода почти гарантировано.
Задача
Необходимо написать скрипт, который бы автоматически генерировал ссылку для авторизации (по данным взятым с страници провайдера) и каждые 40 секунд делал запрос по этой ссылке.
Разбор полетов
После того как в нужные формы пользователь ввел данные формируется ссылка следующего формата:
http://stat.pautina-nau.net/cgi-bin/stat.pl?ses=11111111&a=98&uu=111111&pp=123456789abcdef123456789abcdef00
? - указывает конец ссылки и дальше передаются переменные.
& - разлелитель между переменными.
Какие выводы?
  1. скрипт который обрабатывает данные stat.pl
  2. переменная с именем пользователя (логином) uu.
  3. Переменная котороая содержит пароль pp.
  4. Неизвестная переменная со значением a=98
  5. Неизвестная переменная со значением ses=11111111
Посмотрим в как это описывается в разметке страницы? Для начала просмотрим
страницу в которой находятся формы для логина и пароля:

<form method=get action='/cgi-bin/stat.pl' onsubmit='pp.value=hex_md5(ses.value+" "+pp.value); return true'>
<input type=hidden name=ses value=201110>
<table class='tbg1'><tr class='head'><td align=center colspan=3>
<input type=hidden name=a value=98>Если вы хотите получить доступ в интернет без 
авторизатора - авторизуйтесь в этом окне. 
После авторизации не закрывайте окно - оно будет периодически авторизовываться на 
сервере и поддерживать открытым доступ в интернет</td></tr>
<tr class=row2><td align=right width='33%'>Логин:</td><td><input type=text name=uu size=25 value='' tabindex=4></td> <td rowspan=2 class=row2>
<input type=submit value=' Авторизоваться ' tabindex=6></td></tr><tr class=row2> <td align=right>Пароль:</td>
<td><input type=password name=pp size=25 value='' tabindex=5></td></tr></table>
Этот кусок исходного кода страницы содержит информацию о интересных для нас формах: логин, пароль и формирователья запросов. Рассматривать код начнем с конца:
<td align=right>Пароль:</td>
<td><input type=password name=pp size=25 value='' tabindex=5></td> 
видно что переменная которая содержит пароль называется pp.
Логин:<input name="uu" size="25" tabindex="4" type="text" value="" />
аналогично, переменная с логином называется uu.

<input type=hidden name=ses value=111111>
А в hiden спрятана переменная  ses=111111
<input type=hidden name=a value=98>
Аналогично спрятана в  hiden другая  переменная  a =98

С остальными переменными понятно, но что же означают ses и а ?
Формирование запроса
<form method=get action='/cgi-bin/stat.pl' onsubmit='pp.value=hex_md5(ses.value+" "+pp.value); return true'>

Для запроса используется метод GET, а формирование ссылки происходит с помощью некой функции hex_md5(), при чем как аргумент пересылается ему строка формата:
[ses] [pp]
То есть состоит из таинственной для нас переменной ses через пробел с паролем pp.
Что такое md5?
MD5 (англ. Message Digest 5) — 128-битный алгоритм хеширования, разработанный профессором Рональдом Л. Ривестом из Массачусетского технологического института (Massachusetts Institute of Technology, MIT) в 1991 году. Предназначен для создания «отпечатков» или «дайджестов» сообщений произвольной длины. Является улучшенной в плане безопасности версией MD4. Зная MD5-образ (называемый также MD5-хеш или MD5-дайджест), невозможно восстановить входное сообщение, так как одному MD5-образу могут соответствовать разные сообщения.
Проще говоря шифрует пароль, да так что в обратную сторону потчти невозможно расшифровать.
Механизм формирования ссылки более-менее понятен: через командную строку отправляются данные о логине пароле, переменная ses, переменная а.
Понаблюдав за переменными, можно сделать вывод:
ses — изменяющая свое значение переменная, правда не чаcто гдето 1-2 раза в сутки, используется для шифрования пароля.
a — статическая, не меняется, скорее всего отвечает за идентификацию необходимой формы.

Теперь самое время уточнить задачу:
  1. Получить код страници с формой для авторизации;
  2. Выделить значение переменной ses;
  3. Сформировать строку для зашифровки;
  4. Используя алгоритм md5 зашифровать строку;
  5. сформировать и выполнить запрос по ссылке.
Реализация
Вот что у мну получилось:

#!/usr/bin/env python
import re
import urllib
import hashlib
import logging
import sys

uu=11111 #user name
pp=1111  #password
a=98     #static variable

#intit dictionary of debug levels
LEVELS = {'-debug': logging.DEBUG,
          '-info': logging.INFO,
          '-warning': logging.WARNING,
          '-error': logging.ERROR,
          '-critical': logging.CRITICAL}
#Check for programmed arguments (arguments passed through command line)
if len(sys.argv) > 1:
    level_name = sys.argv[1]
    level = LEVELS.get(level_name, logging.NOTSET)
    logging.basicConfig(level=level)
#logging.basicConfig(level=logging.DEBUG)


# Get a file-like object for the Python Web site's home page.
f = urllib.urlopen(u"http://stat.pautina-nau.net/cgi-bin/stat.pl?a=101&a=99")
# Read from the object, storing the page's contents in 'html_page'.
html_page = f.read()
f.close()
logging.debug(html_page)
logging.debug("\n ***********************************************")

#find needed value from hidden form using regular expression
match = re.search( ur"name=ses[\s]+value=([0-9]{2,8})", html_page )
ses = match.group(1).encode( "cp1251" )
logging.debug("ses = "+ ses)

#encode password with md5 algorithm
pp_value = hashlib.new('md5')
pp_value.update(str(ses)+" "+str(pp))
pp = pp_value.hexdigest()
logging.debug(pp)

#form executable url
params = urllib.urlencode({'ses': ses, 'a': 98, 'pp': pp})
f = urllib.urlopen("http://stat.pautina-nau.net/cgi-bin/stat.pl?%s" % params)
f.read()
html_page_resp = f.read()
f.close()

Как по мну полезный пример, только не содержит функции обновления каждые 40 секунд. Позже разберусь с таймерами и уже в следующей статье дам более подробное описание кода скрипта.

No comments:

Post a Comment