Конкурс инструкций

Авторизация на любом web-портале из C# приложения

Или возьми то что тебе нужно и забудь спросить разрешения.

1. Зачем это нужно?

Пример 1.

Вам нужно получить данные от web-портала в Вашей программе и отправить эти данные куда-то (sms, email, telegram...), но портал требует прохождение авторизации.

Пример 2.

У Вас есть один или несколько аккаунтов от web-портала. После авторизации Вы получаете доступ к ценной информации. Ваша задача - дать тысячам Ваших клиентов доступ к этой информации.

Общее у этих двух примеров одно: пройти авторизацию на web-портале из Вашего приложения, получить необходимые куки (cookies), а далее используя эти куки можно получать необходимую информацию. Если слово cookies для Вас незнакомо, прочитайте здесь:

В рамках данной инструкции будет разобран пример №1. В качестве web-портала выбран https://www.roblox.com - популярный игровой детский ресурс. Задача - получить значение количества игровой валюты на аккаунте.

2. Что мне потребуется?

  • программа для перехвата http/https трафика. Я использую fiddler. Ссылка разработчика https://www.telerik.com/fiddler. Необходимо скачать, установить, разрешить расшифровку https-трафика (рис. 1)

Рисунок 1
  • требуется среда разработки приложения. Я использую Visual Studio 2019 и язык программирования выбрал C#. Для большинства задач достаточно бесплатной версии Visual Studio, скачать можно здесь: https://visualstudio.microsoft.com/ru/vs/
  • желание достичь результата. Чтобы этого добиться, важно придумать интересную идею, и желание придет)

3. Давайте уже начнем.

Чтобы сделать авторизацию на портале из своего приложения нужно чтобы приложение выполнило нужную последовательность https-запросов на портал. Чтобы это сделать, нужно сначала получить список необходимых https-запросов. С данной задачей нам поможет справиться fiddler. Если у Вас еще нет аккаунта от Roblox, зарегистрируйтесь.

Совет: Если есть возможность, всегда создавайте тестовые аккаунты для подобной работы чтобы поберечь свои рабочие аккаунты.

Запускаем программу fiddler, открываем браузер (советую internet explorer 11. Выбор браузера в данном случае - производственная необходимость, а не прихоть. В тех случаях, когда internet explorer не подходит, выберите, например, Google Chrome). В браузере заходим на сайт https://www.roblox.com, производим авторизацию на портале введя логин и пароль. Переключаемся в fiddler, снимаем галочку “Capture Traffic” (рис. 2).

Рисунок 2

Что мы сделали? Мы программой fiddler сняли выполненные https-запросы к порталу. Наша задача теперь – отфильтровать/удалить ненужные запросы. Всего fiddler у меня собрал 635 запросов. Не пугаемся, сейчас их станет намного меньше. На рисунке 3 выделен единственный из первых 38-ми запросов, который следует оставить. Остальные запросы – это:

3-14 запрос: открытие https-туннеля при обращении к сайту. Удаляем.

15, 26, 33, 37, 38: файлы-java-скрипты. Удаляем.

16-18, 21-22, 28-29, 36: файлы css-стилей. Удаляем.

Рисунок 3

После того как мы получили все запросы в fiddler, их нужно раскодировать. Без этого действия мы не увидим корректный html-код ответа. Необходимо выбрать все наши запросы через «Ctrl+A», нажать правой кнопкой мыши по ним и выбрать верхнюю команду «Decode Selected Sessions».

Рассмотрим рисунок 4.

Рисунок 4

Запросы вида 159-160, 167 не относятся к рассматриваемому порталу. Это может быть отправка статистики о посещении сайта или баннеры с рекламой. Удаляем.

Запросы вида 148-149 являются файлами изображений. Удаляем.

Фильтровать ненужные запросы – это навык, который со временем оттачивается.

Удалив все ненужные запросы, получаем картину, представленную на рисунке 5:

Рисунок 5

Взглянем на запрос в строке 2. Правая часть окна fiddler при открытии меню «Inspectors» разделена на две области: верхняя – данные о запросе на сервер, нижняя – данные ответа на этот запрос от сервера. В верхней части интересует всегда вкладка «Headers» - заголовки запроса, в нижней чаще всего «Raw» - это данные ответа. В нижней части выберем «Cookies» - убедимся, что ответ на данный запрос дает нам те куки, которые далее будут отправляться в следующих запросах к порталу, в том числе и в запросе авторизации (рисунок 6).

Рисунок 6

Рассмотрев запрос в строке 289 на рисунке 7, увидим, что запрос отправляет следующие необычные заголовки:

  • X-CSRF-TOKEN: tDMwUMbzcoat (значение меняется при каждом первом обращении к порталу)
  • Origin: https://www.roblox.com (данное значение не меняется, поэтому используем как есть)
Рисунок 7

Копируем значение первого заголовка заголовка «tDMwUMbzcoat», в fiddler выделим любой запрос, нажмем «Ctrl+F» и произведем поиск по нашим запросам. После выполнения поиска увидим следующее (рисунок 8):

Рисунок 8

Fiddler выделил запросы, в которых найден текст нашего поиска. Откроем запрос в строке 2, в нижней части с данными ответа выберем «Raw» и в нижней правой части экрана нажмем «View in Notepad» (рисунок 9).

Рисунок 9

В открывшемся окне блокнота найдем текст «tDMwUMbzcoat» (конечно же, у Вас значение данного ключа будет другим, так как оно уникально для каждого подключения к порталу). Результат поиска на рисунке 10.

Рисунок 10

Делаем вывод: в ответе запроса в строке 2 нужно будет найти значение этого параметра и передать его в запросе в строке 289.

Строка 296: этот запрос в ответе получает новые куки, которые будут использоваться в строке 383.

Строка 383: Выполнив этот запрос, мы получим json-данные, в которых найдем значение количества внутренней валюты на аккаунте (рисунок 11).

Рисунок 11

На этом этапе сбор и анализ данных закончен. Можно приступить к разработке приложения.

4. Разработка приложения.

Рабочие примеры создания авторизаций из кода C# представлены здесь: https://github.com/menshikovyaroslav/Authorizations

Здесь же есть код примера текущей инструкции:

Все используемые методы в коде являются стандартными для Visual Studio, никаких дополнительных библиотек не используется – только те, код которых можно найти в проекте. В коде будут использоваться классы «GetRequest» и «PostRequest» - это обертки для стандартного класса «HttpWebRequest». Будем использовать эти классы для выполнения соответственно get и post запросов. Возьмите код данных классов здесь: https://github.com/menshikovyaroslav/Authorizations/tree/master/Support/CatalogSupportLibrary/Requests

Задача данного этапа – максимально повторить сохраненные в fiddler запросы к порталу. Заранее точно неизвестно, какие запросы важны и какие заголовки важны в запросе.

Подготовка к выполнению запросов:

// Each roblox account has two parameters : User, Password var user = "user"; // !!! rewrite value from your account !!! var password = "password"; // !!! rewrite value from your account !!! // We keep cookies here var cookies = new CookieContainer(); // Any proxy, for example Fiddler var proxy = new WebProxy("127.0.0.1:8888");

В переменные user и password вставим данные нашего аккаунта.

Объект cookies - контейнер куки. В этом объекте будут накапливаться куки при выполнении запросов к web-порталу.

В объект proxy запишите адрес Вашего прокси. "127.0.0.1:8888" - по умолчанию на этом порту работает fiddler.

Строка 2 fiddler: Повторить данный запрос можно с помощью следующего кода:

var getRequest = new GetRequest() { Address = "https://www.roblox.com/", Accept = "text/html, application/xhtml+xml, image/jxr, */*", Host = "www.roblox.com", KeepAlive = true, Proxy = proxy }; getRequest.Run(ref cookies);

В запросе указано:

  • Address - это адрес запроса (чтобы скопировать в fiddler адрес запроса, нужно правой кнопкой мыши по запросу, выбирать «Copy», «Just Url»)
  • Accept - в каком виде будет ответ на запрос (заголовок Accept – рисунок 6, верхняя строка заголовков запроса)
  • Host - в заголовках запроса это нижняя строка. Host можно определить самим: если адрес выглядит https://www.test.com/pages?parameter1=x, то Host - это текст между "//" и "/" после ".com", то есть www.test.com
  • KeepAlive (рисунок 6, вторая строка снизу заголовков запроса). Параметр необязательный, но мы стремимся сделать запрос максимально похожий на оригинальный из браузера. Поэтому если в заголовках запроса видим "Connection: Keep-Alive", то в коде укажем "KeepAlive = true"
  • Proxy - значение прокси сервера, через который будет отправлен запрос. Если Вы хотите отправлять запросы напрямую без прокси-сервера, то замените строку «Proxy = proxy» на «TurnOffProxy = true».

Запросы нужно видеть в отладочных целях. В случае если с первого раза авторизация из кода не получилась, то Вы сможете сверить Ваши запросы с сохраненными запросами из браузера.

На этапе (рисунок 7, 8) мы выяснили, что после выполнения этого запроса нам необходимо найти значение "token". Рассмотрев ответ сервера (рисунок 10), с помощью методов работы с текстом получим значение token:

var tokenStart = getRequest.Response.IndexOf("setToken") + 10; var tokenEnd = getRequest.Response.IndexOf("'", tokenStart); var token = getRequest.Response.Substring(tokenStart, tokenEnd - tokenStart);

Строка 185 fiddler: Повторить данный запрос можно с помощью следующего кода:

getRequest = new GetRequest() { Address = "https://www.roblox.com/login", Accept = "text/html, application/xhtml+xml, image/jxr, */*", Host = "www.roblox.com", KeepAlive = true, Referer = "https://www.roblox.com/", Proxy = proxy }; getRequest.Run(ref cookies);

В отличие от строки 2 fiddler, данный запрос отправляет дополнительно заголовок «Referer» - это значение нужно серверу чтобы знать с какой страницы Вы делаете данный запрос. В нашем случае значение Referer будет равно значению Address из предыдущего запроса.

Строка 289 fiddler: запрос можно повторить с помощью следующего кода:

// Auth request var data = $"{{\"cvalue\":\"{WebUtility.UrlEncode(user)}\",\"ctype\":\"Username\",\"password\":\"{WebUtility.UrlEncode(password)}\"}}"; var postRequest = new PostRequest() { Data = data, Address = $"https://auth.roblox.com/v2/login", Accept = "application/json, text/plain, */*", Host = "auth.roblox.com", ContentType = "application/json;charset=utf-8", Referer = "https://www.roblox.com/login", KeepAlive = true, Proxy = proxy }; postRequest.AddHeader("X-CSRF-TOKEN", token); postRequest.AddHeader("Origin", "https://www.roblox.com"); postRequest.Run(ref cookies);

Обращаем внимание, что для предыдущих двух запросов мы создавали экземпляры классов GetRequest. В данном случае у нас post-запрос, значит создаем экземпляр класса PostRequest. Переменная data – тело post-запроса, значение нужно взять в fiddler из данных запроса (вкладка меню «TextView», рисунок 12).

Рисунок 12

Параметр "ContentType" - обязательный для post-запроса. Он определяет что за содержимое передается в Data. В данном случае передается json-объект.

Переменные user и password передаем в виде «WebUtility.UrlEncode(user)». Это нужно для случаев когда Ваш логин или пароль включает некоторые символы, такие как: ‘+’, ‘=’, ‘/’, ‘@’ (приведена часть символов для примера). Так мы получаем строки, которые можно передавать в теле post-запроса.

Дополнительно к стандартным заголовкам нужно добавить заголовки «X-CSRF-TOKEN» и «Origin». Значение первого мы взяли из первого get-запроса.

В коде ответа данного запроса нам необходимо найти значение параметра "id", оно пригодится при выполнении последнего запроса (строка 383).

var idStart = postRequest.Response.IndexOf("id") + 4; var idEnd = postRequest.Response.IndexOf(",", idStart); var id = postRequest.Response.Substring(idStart, idEnd - idStart);

Рассмотрим рисунок 5:

Запрос в строке 293 получает код 302 (столбец Result) – это значит что запрос перенаправлен на другую страницу. Ознакомившись со следующей документацией:

Находим:

302 Moved Temporarily — запрошенный документ временно доступен по другому URI, указанному в заголовке в поле Location. На рисунке 13 находим Location в ответе и видим на какую страницу происходит перенаправление: https://web.roblox.com/home?nl=true, а это адрес следующего запроса в строке 296.

Следует обратить внимание, что таких перенаправлений может быть множество, а в getrequest.Response при выполнении данного запроса мы получим html-код из последнего запроса в данных перенаправлениях. В нашем случае, мы увидим html-код строки 296. Если нужно отключить перенаправление, то в классах GetRequest и PostRequest предусмотрен параметр «AllowAutoRedirect», который нужно установить в значение false.

Рисунок 13

Строка 296 fiddler:

После выполнений тестов работы программы обнаружено, что запросы 293, 296 не нужны для получения результата последнего 383 запроса. Поэтому и в код включать не нужно эти запросы.

Напомню, данная ситуация вполне обычная, так как мы не можем знать какие из запросов важны. Пока не сделаешь все запросы не поймешь это.

Строка 383 fiddler:

getRequest = new GetRequest() { Address = $"https://economy.roblox.com/v1/users/{id}/currency", Accept = "application/json, text/plain, */*", Host = "economy.roblox.com", KeepAlive = true, Referer = "https://web.roblox.com/home?nl=true", Proxy = proxy }; getRequest.Run(ref cookies);

В строку адреса необходимо добавить значение переменной «id», найденное в html-коде запроса из строки 289 fiddler.

Результат выполнения данного запроса приведен на рисунке 14. Видим, что на счету данного аккаунта 2 robux.

Рисунок 14

Видя такой интересный способ получения количества валюты по указанному id пользователя, приходит мысль проверить, а можно ли авторизоваться под одним пользователем, а получить информацию другого пользователя? К сожалению (или к счастью) данный момент на портале был продуман, и если отправить id другого пользователя, то портал вернет ошибку (рисунок 15).

Рисунок 15

5. Какие сделаем выводы?

Любую необходимую Вам информацию можно получить из кода Вашего приложения. Многие порталы принимают большие усилия чтобы данный процесс усложнить, а значит уйдет большее количество времени для повторения запросов из кода. Важно оценить свое время, стоит ли оно преодоления данных преград.

0
7 комментариев
Написать комментарий...
Make Luv

Зачем?

Ответить
Развернуть ветку
Роман Барабанов

Например если хочешь парсить данные, но нужна авторизация.

Ответить
Развернуть ветку
Yaroslav Menshikov
Автор

Вопрос задайте плиз более подробный чтобы не гадать )

Ответить
Развернуть ветку
GAME CHANELL

А если у меня нету токена как на рисунке 7 при авторизации на другом сайте 

Ответить
Развернуть ветку
GAME CHANELL

откуда взялись номера строк, 2 289,296,383

Ответить
Развернуть ветку
Yaroslav Menshikov
Автор

Fiddler собирает все запросы которые у Вас отправляются. Запросы попадают с разных порталов, поэтому нумерация в данном случае стихийна. У Вас будет другая нумерация. Главное не номер строки, а отфильтровать ненужные запросы, чтобы остались несколько нужных.

Ответить
Развернуть ветку
123 456

Зачем писать статью, базируясь на технологиях, устаревших вместе с .NET Framework черт знает сколько лет назад? Асинхронное программирование? HttpClient? Документацию бы хоть по WebRequest открыли, там русским по белому написано про это.

Ответить
Развернуть ветку
4 комментария
Раскрывать всегда