пятница, 27 октября 2017 г.

parser

Авторизация на сайте при помощи библиотеки cUrl Продолжая цикл статей посвященный парсерам и всем, что с ними связано. В этой статье расскажу про то, как легко можно авторизоваться на любом сайте при помощи библиотеку cUrl php. Для примера я взял один Украинский портал, к которому я собственно и буду подбирать ключики. Для работы нам также понадобится библиотека simple_html_dom 
Авторизацию будем проходить на сайте http://tender.me.gov.ua , не буду создавать лишних ссылок, чтобы не накрыть нашу лавочку. 
Для начала зарегистрируйтесь на сайте, чтобы иметь тестовые логин-пароль для входа на сайт. 
На сайте мы видим ничем не примечательную форму авторизации.
Форма авторизации на сайте
"Внутренности" у нее тоже ничем не примечательны
<form id="login" name="login" method="post" action="/EDZFrontOffice/menu/uk/;jsessionid=c4651e96f5c2b9afb08776cbd1a5" enctype="application/x-www-form-urlencoded">
 <input type="hidden" name="login" value="login" />
 <input id="login:login" type="text" name="login:login" class="login" maxlength="80" />
 <input id="login:password" type="password" name="login:password" value="" maxlength="80" class="login" />
 <input type="image" src="images/t_login_button.png;jsessionid=c4651e96f5c2b9afb08776cbd1a5" name="login:j_id_id254" alt="&#1042;&#1093;&#1110;&#1076;" />
 <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id1:j_id2" autocomplete="off" />
</form>
Итак смотрим, что форма авторизации отправляет при аутентификации. Для этого возпользуемся  Google Chrome, открываем в нем сайт, затем Инструменты->Инструменты разработчика. Далее переходим на вкладку Network. Для браузера Opera Меню->Страница->Средства Разработки->Open Opera DragOnFly и вкладка Сеть. Хоть я и фанат Оперы, но на мой взгляд продукт от компании Google немного удобнее. Я конечно не про браузер =)
Теперь авторизуемся на сайте, смотрим, что посылает форма при сабмите. 
Просмотр отправляемых данных в Google Chrome
Видим, что форма посылает следующие параметры
1
2
3
4
5
6
login:login
login:login:{loginmail}
login:password:{password}
login:j_id_id254.x:8
login:j_id_id254.y:7
javax.faces.ViewState:j_id1:j_id2
Попробуем решить задачу в лоб и отправить эти данные простым массивом. Сразу учтем, что данные отправляются на защищенный ssl url, по протоколу https. Это следует учитывать при отправке данных в cUrl. Рассказывать, как работает SSL в рамках данной статьи я не стану, скажу лишь, что сертификаты, которыми обмениваются две стороны, сами по себе ничего не говорят о его обладателе. Они нужны лишь для того, чтобы передать открытый ключ, обеих сторон и служат для шифрования канала связи. Т.е. мы можем проверять наличие ssl, и подключать сертификат в cUrl, но это будет служить пользу только нам, сервер не может узнать пользуемся ли мы сертификатом, или используем незащищенный канал связи.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//О том, что мы авторизовались будем судить по наличию формы logout
function isAuth( $data ){
  return preg_match('#<form[^>]+id="logout"#Usi',$data);
}
$ch = curl_init();
$url 'https://tender.me.gov.ua/EDZFrontOffice/menu/uk/';
curl_setopt($ch, CURLOPT_URL, $url ); // отправляем на
curl_setopt($ch, CURLOPT_HEADER, 0); // пустые заголовки
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // возвратить то что вернул сервер
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // следовать за редиректами
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);// таймаут4
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);// просто отключаем проверку сертификата
curl_setopt($ch, CURLOPT_POST, 1); // использовать данные в post
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
  'login'=>'login',
  'login:login'=>'mylogin@sitename.ru',
  'login:password'=>'password',
  'login:j_id_id254.x'=>8,
  'login:j_id_id254.y'=>7,
  'javax.faces.ViewState'=>'j_id1:j_id2',
));
echo isAuth($data = curl_exec($ch))?'Success':'Failed';
curl_close($ch);
Запускаем и видим, что ничего не получилось. Сайт просто так не сдался. Мы не учли одну вещь, данные об авторизации должны быть сохранены в cookie. Для этого добавим две строчки в код, к примеру после curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
1
2
3
4
5
...
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_COOKIEJAR, dirname(__FILE__).'/cookie.txt'); // сохранять куки в файл
curl_setopt($ch, CURLOPT_COOKIEFILE,  dirname(__FILE__).'/cookie.txt');
...
И снова мимо. Обратим внимание, что форма помимо стандартных логина-пароля, отправляет еще 3 динамических поля. Данные в них все время разные и генерируются при обновлении страницы. Значит, нужно загрузить страницу, скопировать эти данные и проходить аутентификацию уже с ними. Для этого немного "облагородим" код, заключив запрос данных с сервера cUrl'ом в отдельную функцию. Все отличие get запроса от post, заключается в том, что при пост запросе отправляются данные CURLOPT_POSTFIELDS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function request($url,$post = 0){
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url ); // отправляем на
  curl_setopt($ch, CURLOPT_HEADER, 0); // пустые заголовки
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // возвратить то что вернул сервер
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // следовать за редиректами
  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);// таймаут4
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  curl_setopt($ch, CURLOPT_COOKIEJAR, dirname(__FILE__).'/cookie.txt'); // сохранять куки в файл
  curl_setopt($ch, CURLOPT_COOKIEFILE,  dirname(__FILE__).'/cookie.txt');
  curl_setopt($ch, CURLOPT_POST, $post!==0 ); // использовать данные в post
  if($post)
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
  $data = curl_exec($ch);
  curl_close($ch);
  return $data;
}
Попытаемся выдернуть значения переменных полей 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$data = request('https://tender.me.gov.ua/EDZFrontOffice/');
include 'simple_html_dom.php';
$data = str_get_html($data);
$auth array(
    'login'=>'login',
    'login:login'=>'***************',
    'login:password'=>'************l',
    'login:j_id_id254.x'=>$data->find('input[name="login:j_id_id254.x"]',0)->value,
    'login:j_id_id254.y'=>$data->find('input[name="login:j_id_id254.y"]',0)->value,
    'javax.faces.ViewState'=>$data->find('input[name="javax.faces.ViewState"]',0)->value,
);
$data->clear();
unset($data);
print_r($auth);
Оказывается, что поле login:j_id_id254.x и поле login:j_id_id254.y создаются и заполняются js скриптом. Попробуем оставить их как были 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$url 'https://tender.me.gov.ua/EDZFrontOffice/menu/uk/';
$data = request('https://tender.me.gov.ua/EDZFrontOffice/');
include 'simple_html_dom.php';
$data = str_get_html($data);
$auth array(
    'login'=>'login',
    'login:login'=>'site@sitename.ru',
    'login:password'=>'password',
    'login:j_id_id254.x'=>10,
    'login:j_id_id254.y'=>11,
    'javax.faces.ViewState'=>$data->find('input[name="javax.faces.ViewState"]',0)->value,
);
$data->clear();
unset($data);
echo isAuth(request($url,$auth))?'Success':'Failed';;
Вот  и все, сайт побежден. Далее работаем с ним, как с обычным сайтом. Данные о том, что Ваш скрипт никто иной, как авторизованный пользователь бережно хранятся в cookie.   

Комментариев нет:

Отправить комментарий

JavaScript learn

Чтобы вставить элемент после какого-то элемента, нужно создать прототип. Element.prototype.appendAfter = function (element) { element.paren...