Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Регистрация Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > PainKiller

Оценить эту запись

Имитируем браузер (на примере залогинивания из приложения в контакт)

Запись от PainKiller размещена 28.08.2014 в 13:38

Некоторое время назад я задумался насколько реально получить access_token для приложения вконтакте без браузера. Мой вопрос на эту тему на форуме остался без ответа и я начал копать сам. Надо сказать, что решение этой проблемы отняло у меня приличное количество времени, зато в результате я многому научился, чем и спешу поделиться. Как обычно говорят в таких случаях - представляемая информация дается для ознакомления, автор не несет ответственности за её использование в незаконных целях (написание спам-ботов, угон аккаунтов у пользователей и т.п.). Тем более, что она и так доступна в интернете, и я не открываю ничего нового, просто привожу свой опыт.
На самом деле решить эту задачу можно двумя путями – простым и сложным. Начнем со сложного, он первым приходит в голову, и его реализации на различных языках я нагуглил первыми. Он заключается в том, чтобы отследить в консоли браузера и затем имитировать все запросы, с соответствующими заголовками, идущие от клиента серверу. Таким образом, если все сделать правильно, сервер не сможет определить, с кем он общается с реальным человеком за открытым браузером или с программой. С примером такой реализации залогинивания вконтакт на php можно ознакомиться здесь (он не рабочий, но логика действий ясна). Создатели веб-приложений прекрасно знают об этом способе и всячески стараются его осложнить, вставляя в страницы javascript’ы которые динамически изменяют страницу после загрузки, подгружают контент шифрованными ajax запросами и т.д. То есть теоретически этот способ работает всегда, но реализация его на практике может оказаться очень и очень геморной задачей. Тем не менее, я начал с него, и начал на флеше. И тут случился первый облом - нативные классы реализующие общение по http протоколу, релизуют его в урезанном виде, и не дают доступа к нужным заголовкам (см. мой пост) Поэтому я обратился к библиотеке As3httpclientlib, а так как авторизация вконтакте идет по протоколу https, использовать её пришлось в паре с as3crypto. Эта пара оказалась довольно глючной, в какой-то момент на мой очередной запрос перестал приходить ответ от сервера, и я решил пересесть на Node.js, тем более что приложение у меня и так предполагается клиент-серверное (Node.js + AIR). На ноде все оказалось проще, глюков не было, но мне просто не хватило терпения идеально сымитировать все нужные запросы, и контакт начинал меня зацикливать (удалял ранее выданные куки, и редиректил на форму авторизации). Плюс на тот момент я уже узнал о втором более простом способе обмана сервера, и решил освоить его (тем более что он еще пригодится и в других проектах). Этот более простой способ заключается в использовании «безголового браузера» т.е. браузера не имеющего GUI (можно только снимать скриншоты с загружаемых страниц) и управляемого из командной строки. Самый популярный на данный момент проект такого типа это phantomJS я начал с него и он у меня не пошел. Хоть я и оцениваю свои знания JavaScript на твердую четверку, его API оказалось для меня слишком суровым, каждое движение на загружаемой странице там выполняется асинхронно по коллбеку, и при написании даже не сильно сложного кода быстро оказываешься в аду вложенных коллбеков, который взрывает мозг. Поэтому я переключился на более приятную надстройку над фантомом – casperJS, которая работает с асинхронностью в стиле библиотек async и promises и буквально за 20 – 30 минут решил задачу (и это после стольких неудачных заходов!).
И так алгоритм действий следующий (я делал это все на Windows 7):
1. Устанавливаем phantomJS и прописываем путь к его исполняемому экзешнику в переменной среды PATH. Результатом должно быть исполнение команды phantomjs в командной строке без ошибки.
2. Устанавливаем casperjs для него также понадобится python 2.6. Опять же прописываем путь к его исполняемому экзешнику в переменной PATH. Результатом должно быть исполнение команды casperjs в командной строке без ошибки.
3. Запускаем в командной строке скрипт авторизации. Если мы находимся в директории где лежит скрипт, команда, запускается с 3 аргументами и выглядит так casperjs vk.js path login password где vk.js файл с нашим скриптом, path – url до формы авторизации (как он образуется см. ниже в модуле для node.js) login – логин пользователя, password – пароль пользователя. Код скрипта я прокомментировал и привожу ниже:
Код AS1/AS2:
/*
 	 * Внимание это не модуль Node.js, это
 	 * скрипт исполняемый casperjs
 	 * 
	 * @author PainKiller
	 */
var casper = require('casper').create();
var x = require('casper').selectXPath;
 
//Выставляем правильный userAgent т.к. по умолчанию каспер отдает свой, и это может смутить сервер
casper.userAgent('Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0');
 
/*
 * открываем форму авторизации, путь к ней получаем как 1й аргумент из
 * командной строки, вставляем в форму логин (2й агрумент), и пароль (3й)
 */
casper.start(casper.cli.get(0), function() { 
	//элементы формы выцепляем по XPath, я воспользовался этим способом, т.к. id элементы этой формы не имеют
	this.fillXPath('form', {
	'//*[@id="mcont"]/div/div[2]/form/dl[1]/dd/div/input': casper.cli.get(1),
	'//*[@id="mcont"]/div/div[2]/form/dl[2]/dd/div/input': casper.cli.get(2) }, true);
	//casper.capture('vk1c.png');
 
});
 
/*
 * нажимаем кнопку submit
 */
casper.then(function () {
	//console.log("submitting...");
    this.evaluate(function () {
        $('form').submit();
    });
});
 
/*
 * ждем 5 секунд загрузки редиректа, это я перестраховываюсь
 * может появится еще одно окно подтверждения прав приложения - тогда поступаем с ним также как с предыдущей формой
 */
casper.wait(5000, function(){
	//console.log("wait...");
	var url = this.evaluate(function () {
        return document.URL;
    });
	//выводим в консоль url с полученным токеном
	console.log(url);
	//casper.capture('vk2c.png');
	//закрываем каспер
	casper.exit();
});
 
casper.run();
При ручном запуске скрипта из командной строки можно раскоментировать все отладочные console.log и снятие скриншотов с экранов (вызовы casper.capture()). Я это делал в целях отладки, чтобы убедится что форма правильно заполняется данными и происходит редирект на нужную страницу. При автоматизированном запуске лучше оставить все закомментированным, иначе консоль будет засоряться ненужными строками.
4. Ручное получение токена без браузера это хорошо, но мы же хотели полностью автоматизировать этот процесс)). Поэтому последний пункт - автоматизация запуска скрипта. Для этого я написал модуль Node.js (я знаю про spookyJS но мне было влом переписывать скрипт, да это и не нужно). Скрипт для каспера я просто запускаю в командной строке как дочерний процесс. Привожу его код:
Код AS1/AS2:
/*
 	 * Модуль автоматической авторизации вконтакте, 
 	 * методом vklogin в который передается объект user с логином и паролем 
 	 * пользователя, запускает в командной строке скрипт каспера vk.js,  
 	 * который возвращает url с access_token
 	 * 
	 * @author PainKiller
	 */
var exec = require('child_process').exec,
    child,
    _user;
var p = require('path');
var url = require('url');
var qs = require('querystring');
var util = require("util");
var EventEmitter = require("events").EventEmitter;
 
 
/*
 * Данные приложения, через которое происходит авторизация,
 * необходимы для формирования пути к форме авторизации,
 * подробнее см. доки https://vk.com/dev/auth_mobile
 */
var app = {
	APP_ID : "4039999",
	APP_SECRET : "HPXVooyt0tZZZ6C8tynsv",
	SETTINGS : "wall,friends",
	REDIRECT_URI : "http://oauth.vk.com/blank.html",
	DISPLAY : "mobile",
	API_VERSION : "5.24"
};
 
function VKAuthorizer() {
    EventEmitter.call(this);
 
}
// наследуемся от EventEmitter, чтобы иметь возможность
// испускать событие когда получим токен
util.inherits(VKAuthorizer, EventEmitter);
 
/*
 * функция авторизации, принимает объект user с двумя полями
 * login и password, и при получении токена эмитит событие 
 * "token_received", передающее объект с токеном
 */
VKAuthorizer.prototype.vklogin = function(user) {
	_user = user;
	var self = this;
	var path = "http://oauth.vk.com/authorize?client_id=" + app.APP_ID
			+ "&client_secret=" + app.APP_SECRET + "&scope=" + app.SETTINGS
			+ "&v=" + app.API_VERSION + "&username=" + user.login
			+ "&password=" + user.password + "&redirect_uri="
			+ app.REDIRECT_URI + "&display=mobile&response_type=token";
	console.log(path);
	var pathToJS = p.dirname(process.mainModule.filename);
	pathToJS = pathToJS.replace('\server.js', '');
	pathToJS += '\\utils\\vk.js';
	console.log('pathToJS = ' + pathToJS);
	var command = 'casperjs ' + pathToJS + ' "' + path +  '" ' + user.login + " " + user.password;
	console.log(command);
	child = exec(command,
	  function (error, stdout, stderr) {
		  //забираем часть урла от хеша
		  var query = url.parse(stdout.toString()).hash.replace('#', '');
		  // парсим, и получаем объект с access_token
		  var token = qs.parse(query);
		  self.emit("token_received", token);  
	    if (error !== null) {
	      console.log('exec error: ' + error);
	    }
	});   
};
 
module.exports = exports = VKAuthorizer;
Используется модуль стандартно:
Код AS1/AS2:
var VKAuthorizer = require('./vkAuthorizer');
var auth = new VKAuthorizer();
 
…
 
auth.on("token_received", function(token){
		//делаем что то с полученым токеном
 
});
auth.vklogin(user);
Т.е. в слушатель приходит объект token вида {"access_token":"efa09dea7b0e397bdd9d3d41ee9ccb4eb342af334c1fb6e7ca36cdaadd07c0f157baedf9473b6c88b77c0","expires_in":"86400","user_id":"12656798"}, с которым мы можем дальше выполнять запросы к API вконтакте.
Автоматический запуск скрипта для каспера можно также делать и напрямую из AIR приложения, для этого есть класс NativeProcess
Есть только один маленький ньюанс. Дело в том, что класс NativeProcess позволяет запускать только экзешники и передавать им аргументы. Для меня это не очень удобно, т.к. нужно знать расположение экзешника casperjs, а при переносе проекта на другую машину оно может изменяться. Поэтому для исполнения скрипта casperJS из под AIR лучше воспользоваться этой библиотекой Она использует прокси-экзешник написанный на C++, который перенаправляет аргументы командной строки из AIR в командную строку, и позволяет вызывать, в том числе и системные команды. Экзешник можно положить в директорию приложения и вызывать его оттуда. Но саму эту либу я еще не использовал, поэтому не знаю насколько это все работоспособно, это только мои теоретические измышления.
ну и небольшой эпилог, на самом деле это очень полезная технология, и если включить фантазию ей можно найти очень интересное применение, не только связанное с написанием спам-ботов (web-scraping, использование из своего приложения каких угодно веб-приложений, автоматизированные тесты веб-страниц и т.д.), буду рад если кто нибудь поделится своими идеями на этот счет.
Всего комментариев 15

Комментарии

Старый 28.08.2014 15:57 Котяра вне форума
Котяра
 
Аватар для Котяра
Цитата:
Есть только один маленький ньюанс. Дело в том, что класс NativeProcess позволяет запускать только экзешники и передавать им аргументы. Для меня это не очень удобно, т.к. нужно знать расположение экзешника
При упаковке клади нужные exe/dll в корень приложения, тогда они будут в applicationDirectory
потом при вызове просто формируешь полный путь
Код AS3:
var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo(); 
			var file:File = File.applicationDirectory.resolvePath("my.exe"); 
			nativeProcessStartupInfo.executable = file;
Старый 28.08.2014 16:24 PainKiller вне форума
PainKiller
 
Аватар для PainKiller
Прокси-экзешник я так и предполагал искать таким образом из директории приложения, только не привел код.
Если же вы имеете в виду что туда же можно положить экзешники phantomjs и casperjs то меня смушает то что casper должен знать путь до фантома, я нашел в сети пример, где путь до фантома прописывался в скрипте каспера через метод phantom.casperPath(), подробнее можно тут почитать но все равно не уверен, что такую конфигурацию удастся запустить хотя попробовать можно.
Обновил(-а) PainKiller 28.08.2014 в 17:10 (Неточность ответа)
Старый 28.08.2014 23:33 alexcon314 вне форума
alexcon314
Цитата:
Устанавливаем casperjs для него также понадобится python 2.6.
Так на целевую машину еще и питон тащить, если я правильно понял? Не айсс, однако!
Старый 29.08.2014 10:57 PainKiller вне форума
PainKiller
 
Аватар для PainKiller
Ну а что делать, это быстрое и комфортное решение, но для его работы требуется окружение с кучей фарша, это его минус согласен)))
Старый 29.08.2014 12:12 alexcon314 вне форума
alexcon314
Немного скепсиса все же. "обман приложений" - всегда условно, коряво, дыряво и в итоге непотребно. Но скилл прокачать стоит, да.
Старый 01.09.2014 03:55 nubideus вне форума
nubideus
почему webkit отдает user agent огнелиса?
Старый 01.09.2014 10:41 PainKiller вне форума
PainKiller
 
Аватар для PainKiller
Цитата:
почему webkit отдает user agent огнелиса
Не понял вопрос... В каспере я прописал юзер агент лисы, что бы подделаться под реальный браузер, если вам это не нравится впишите что то другое.
Старый 04.09.2014 21:00 dimarik вне форума
dimarik
 
Аватар для dimarik
Я так понял, что так можно любую стенку в магазине, т.е. вконткте угнать? Круто!
Старый 08.09.2014 11:50 PainKiller вне форума
PainKiller
 
Аватар для PainKiller
Цитата:
Я так понял, что так можно любую стенку в магазине, т.е. вконткте угнать? Круто!
Да в том то и прикол, насколько просто и быстро это делается.
И в продолжение темы, узнал про еще один вариант "подделки браузера" для node.js - WebDriverJs это адаптация selenium для ноды, асинхронность разруливается с помощью promises. Поддерживает прокси, и много чего другого. Выглядит очень удобно.
Старый 16.09.2014 20:20 dimarik вне форума
dimarik
 
Аватар для dimarik
Круто. Маилру купила вконтактик. Может быть они починят эту дыру.
Старый 18.09.2014 20:49 PainKiller вне форума
PainKiller
 
Аватар для PainKiller
А как её можно починить вообще в принципе? Я на эту тему думал, можно наверное как то программно менять айдишники элементов формы (и синхронно рассчитывать и принимать их на сервере), что бы их нельзя было выцепить из каспера - но я что то как то не представляю реализацию, и взломать это все равно можно будет, поковырявшись с яваскриптом. И их все равно можно будет выцепить по CSS свойствам, стилизовать форму они же будут. Я видел очень навороченные защиты от того же web-scrapingа и все равно народ обходит их без проблем.
Старый 22.09.2014 20:56 dimarik вне форума
dimarik
 
Аватар для dimarik
Кхм. Я чувствую недопонимание между нами. Я немного перефразирую свой вопрос про стенку. Если я не знаю пароль от аккаунта, то с помощью описанной вами техники я могу легко зайти под его владельцем?
Старый 22.09.2014 22:40 PainKiller вне форума
PainKiller
 
Аватар для PainKiller
Нет, под владельцем аккаунта вы зайти не сможете, для этого нужно знать логин и пароль от аккаунта.
Старый 22.09.2014 22:41 PainKiller вне форума
PainKiller
 
Аватар для PainKiller
Это всего лишь техника получения access_token минуя браузер)) Не более того.
Старый 09.10.2014 17:45 PainKiller вне форума
PainKiller
 
Аватар для PainKiller
Еще одну охрененную обертку для phantomjs нашел http://www.nightmarejs.org/ Пожалуй она мне больше всех нравится)))
 

 


Часовой пояс GMT +4, время: 12:47.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2020, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.