- Регистрация
- 23 Август 2023
- Сообщения
- 3 641
- Лучшие ответы
- 0
- Реакции
- 0
- Баллы
- 243
Offline
Решаемая задача
Язык VBA встроен во все офисные приложения, не смотря на свою кажущуюся устарелость, востребован при анализе информации, особенно числовой, финансовой. Это особенно востребовано в больших организациях, которые просто не успевают воплотить идеи работников по автоматизации их работы. Обычно, анализом занимаются подразделения, отвечающие за составление отчетности. Автоматизация их работы никогда не является приоритетом, не помогает даже RPA. Работникам, как и прежде, удобно воспользоваться Excel, Outlook для решения своих задач, а также автоматизировать рутинные операции с использованием языка VBA.
Перед тем, как анализировать информацию на VBA, ее необходимо собрать из разных источников. Самый простой механизм аутентификации – Basic. Он описан во множестве статей. Но в корпоративной экосистеме для аутентификации пользователей чаще применяют Kerberos аутентификацию, а в последнее время стала популярной аутентификация по протоколу OpenIdConnect (OIDC).
В статье рассмотрим методику анализа протокола аутентификации OpenIdConnect и самого популярного механизма авторизации – Authorization code flow, а также разработку приложения на языке VBA, осуществляющего за пользователя такую аутентификацию для последующего сбора данных из корпоративных систем. В статье есть код, который можно переиспользовать, но ввиду отличий в корпоративных инфраструктурах организаций, начать необходимо с анализа контекста организации, применяемых настроек протокола аутентификации.
Решая аналогичную проблему, у меня не получилось найти готовых решений ни на русском, ни на английском языке. Кроме Basic аутентификации на VBA, иной информации нет. Пришлось пройти путем экспериментов и потратить около 8 часов на изучение вопроса и воспроизведение протокола аутентификации OIDC на языке VBA. Надеюсь, информация в этой статье позволит сэкономить ваше время. Статья будет полезна как опытным разносторонним разработчикам с широким кругозором, а также начинающим специалистам по VBA без опыта web разработки. Те моменты, которые могут бы не интуитивны, особенно важны по тексту, заняли время на эксперименты, я выделю жирным. Сразу предупреждаю, что серебряной пули в статье нет, не проведя анализ вашего приложения, просто скопировать код и использовать не получится. Код потребует адаптации.
Анализируем протокол аутентификации
Возможности анализа в корпоративной инфраструктуре часто ограничены. Для анализа воспользуемся встроенными в браузер инструментами. В Chrome и Яндекс браузере - это функция «Исследовать элемент». В Edge – «Проверить». Дальнейшие названия буду приводить на примере Яндекс браузера.
Вначале, посмотрим на аутентификацию в нашем сервисе, назовем его ABC, глазами обычного пользователя. Открываем браузер в режиме инкогнито. Открываем сервис ABC. Протокол OpenIdConnect предполагает перенаправление пользователя для ввода пароля на страницу провайдера аутентификации, назовем его IDP. Эти два названия дальше будем часто использовать, стоит их запомнить (ABC, IDP). IDP предложит пользователю ввести логин и пароль. После ввода логина и пароля, браузер вернет нас обратно на страницу сервиса ABC, где мы увидим интересующие нас данные. Если сеть быстрая, то в большинстве случаем мы увидим 2 действия: страницу аутентификации IDP и страницу сервиса ABC с данными, но на самом деле, действий было гораздо больше. Часто, в корпоративной инфраструктуре используется Kerberos аутентификация, поэтому действий может быть 3. Браузер может дважды спросить пароль.
Подготовимся к анализу трафика. Опытным путем, самым удобным, в данном случае, способом упрощенно зафиксировать происходящее в сети является sequence диаграмма процесса. Хотя бы на бумаге нарисуйте 3х действующих лиц (браузер, ресурс ABC, IDP).
Попробуем разобраться, исследовать сетевой трафик. Закрываем браузер (надо обнулить куки), открываем его заново в режиме инкогнито и заходим на любой сайт, ждем его загрузки. Включаем режим исследования. Там, переключаемся на вкладку Network. Переходим страницу сервиса ABC, браузер опять отобразит страницу аутентификации IDP, но на вкладке Network появится множество строк, обозначающих перехваченные сетевые пакеты. Введем логин и пароль, дождемся загрузки ресурса ABC и остановим перехват трафика в браузере.
Проанализируем перехваченный трафик. Сетевых пакетов много, параметров в них тоже много. Не все пакеты нам интересны, не все параметры имеют отношение к аутентификации. Когда браузер пытается загрузить пользовательский интерфейс страницы ABC, то он скачает как саму страницу, так и различные картинки, css файлы, файлы javascript. Они нас не интересуют. Пойдем сверху вниз, пока не найдем следующие пакеты:
1. Вначале, когда браузер сделает запрос на сервис ABC, тот проверит, есть ли у пользователя токен для аутентификации в блоке cookie. Если токена нет, а в режиме инкогнито его действительно не будет, сервис ABC переправит пользователя на свою страницу аутентификации. Т.е. он может не сразу направить пользователя на IDP, а сначала направить на свой сервис аутентификации. URL такого сервиса может быть: ABC/login например. Т.е. уже на первом шаге есть опциональность, зависящая от конкретного сервиса, его настроек.
2. Сервис аутентификации ABC, если там настроен OpenIdConnect, перенаправит пользователя на IDP, передав в IDP конкретный адрес сервиса ABC, на который надо вернуть пользователя после прохождения аутентификации. Мы не всегда приходим на главную страницу сервиса ABC, у нас может быть ссылка на какой-то внутренний раздел. Смысл такого действия, чтобы пользователя вернуть на страницу, которую он запросил, а не домашнюю страницу сервиса ABC.
3. Часто, для удобства работы пользователей, корпоративные веб приложения используют Kerberos аутентификацию совместно с OpenIdConnect. Опять опциональность. Если она настроена, если тикетов в операционной системе нет, вы не в домене или в режиме инкогнито, то вверху в браузере вы увидите не типичное невзрачное окошко для ввода логина и пароля:
Cкриншот 1. Окно Kerberos аутентификации
В перехвате могут быть несколько сетевых пакетов связанных с попыткой Kerberos входа. Сложность анализа Kerberos в том, что браузер не видит некоторые Kerberos пакеты, т.к. браузер обращается к API операционной системы для прохождения Kerberos аутентификации (это еще один участник процесса). Мы только увидим, что браузер отправляет пакеты аутентификации, но не сможем понять откуда взялись данные у браузера для генерации этих пакетов. Для полноценного анализа Kerberos нужен снифер, но не каждому разрешат поставить его на рабочий компьютер и для достижения цели по OIDC нам это не очень нужно.
4. После попытки аутентификации в Kerberos, нас перекинет на форму аутентификации IDP. Отобразится обычные web форма аутентификации, основанная на теге form.
Cкриншот 2. Аутентификация через обычную web форму (тег form)
Воспроизводим OPENIDCONNECT на языке VBA, получаем тикет. Важные моменты
Весь вышеуказанный процесс необходимо воспроизвести шаг за шагом. Разработку удобно вести в отдельном модуле. Количество шагов на sequence диаграмме и в коде немного отличаются. Но если вы поняли смысл, это не должно доставить сложностей.
Для отладки лучше записывать данные сетевых пакетов в файл, потому самостоятельно. В код включены функции записи информации в файл (myFILE.CreateAfile, myFILE.AppendAFile), подготовьте эти функции самостоятельно, либо удалите их вызов из кода. Использование MsgBox и запись в ячейки EXCEL не удобно для отладки, сетевые пакеты могут быть очень большими.
В коде будет использована библиотека Microsoft WinHttp.WinHttpRequest.5.1.
Особое внимание нужно обратить на выключение редиректа - http.Option(6) = False. Если его не выключить, перенаправления произойдут не явно, вы не сможете прочитать важные параметры промежуточных пакетов и их Cookie, необходимые для дальнейших запросов.
Еще один важный момент – это проставление заголовка с длинной пакета. Если поленитесь его указать, можно потратить много времени на выяснения причины непредвиденных ответов сервера. Используем http.SetRequestHeader "Content-length", "" & ____.
И третий момент, с которым я столкнулся, проверяйте url на который ведет перенаправление. Там могут быть закодированные через апмперсант символы, кодирующие спец.знаки в строке url и не воспринимаемых библиотекой WinHttp.WinHttpRequest.5.1.
В остальном, надо просто воспроизвести то, что видим в перехвате трафика.
Представленный код не претендует на оптимальность, там могут быть не используемые переменные и последовательность отправки пакетов писалась для конкретного корпоративного сервиса.
Определяем переменные
Dim ABCCookies As String
Dim ACTION As String
Dim AUTH_SESSION_ID As String
Dim AUTH_SESSION_ID_LEGACY As String
Dim KC_RESTART As String
'куки получаемые от ABC сайта
Dim AspNetNonce As String
Dim AspNetCorrelation As String
Dim session_code As String
Dim execution As String
Dim tab_id As String
'куки IDP после успешной аутентификации
Dim IDP_IDENTITY As String
Dim IDP_IDENTITY_LEGACY As String
Dim IDP_SESSION As String
Dim IDP_SESSION_LEGACY As String
'код авторизации от IDP
Dim code As String
Dim state As String
Dim session_state As String
'искомый токен ABC
Dim ABC_Cookie As String
Dim ABC_Cookie_Period As String
Основная функция
Function get_ABC_Data(authUser As String, authPass As String, URL As String) As String
Dim ret As String
Dim r As String
myFILE.permitedWrite = True 'включаем логирование
r = myFILE.CreateAfile("C:\Users\USERNAME\Desktop\vba\log.txt", "Log ABC Auth")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
If ABC_Cookie = "" Then
ret = get_1_ABCCookie(URL) 'запрашиваем куки ABC и ссылку на провайдер аутентификации
ret = get_2_IDPGetCookie1 'get запрос аутентификации, получаем куки idp
ret = get_3_IDPGetCookie2 'повторно
ret = get_4_IDPPost1 'получаем форму ввода пароля
ret = get_5_IDPPost2(authUser, authPass) 'получаем код авторизации, который на сл.шаге меняем на токен
ret = get_6_ABCAuth 'получаем токен от ABC
End If
myFILE.permitedWrite = False 'выключаем логирование
get_ABC_Data = ""
End Function
Функции вызова конкретных пакетов flow
Private Function get_1_ABCCookie(URL As String) As String
Dim location As String
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
http.Open "GET", URL, False
http.SetRequestHeader "Connection", "keep-alive"
http.Option(6) = False 'выключаем редирект, чтобы получить куки от ABC
Call http.Send
http.waitForResponse 5
'получаем куки
AspNetNonce = ".AspNetCore.OpenIdConnect.Nonce." & Split(Split(http.GetAllResponseHeaders, ".AspNetCore.OpenIdConnect.Nonce.")(1), ";")(0)
AspNetCorrelation = ".AspNetCore.Correlation." & Split(Split(http.GetAllResponseHeaders, ".AspNetCore.Correlation.")(1), ";")(0)
'получаем адрес перенаправления для следующего запроса
ACTION = http.GetResponseHeader("Location")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 1 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "GET: " & URL)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 1 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_1_ABCCookie = "" & http.status
End Function
Private Function get_2_IDPGetCookie1(Optional authUser As String = vbNullString) As String
Dim location As String
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
URL = ACTION
http.Open "GET", URL, False
http.Option(6) = False 'выключаем редирект, чтобы получить куки от ABC
http.SetRequestHeader "Connection", "keep-alive"
Call http.Send
http.waitForResponse 5
'location = http.GetResponseHeader("Location")
'получаем куки
AUTH_SESSION_ID = Split(Split(http.GetAllResponseHeaders, "AUTH_SESSION_ID=")(1), ";")(0)
AUTH_SESSION_ID_LEGACY = Split(Split(http.GetAllResponseHeaders, "AUTH_SESSION_ID_LEGACY=")(1), ";")(0)
KC_RESTART = Split(Split(http.GetAllResponseHeaders, "KC_RESTART=")(1), ";")(0)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 2 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "GET: " & URL)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 2 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_2_IDPGetCookie1 = "" & http.status
End Function
Private Function get_3_IDPGetCookie2(Optional authUser As String = vbNullString) As String
Dim location As String
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
URL = ACTION
cookie = ""
cookie = cookie & "AUTH_SESSION_ID=" & AUTH_SESSION_ID & ";" '& vbNewLine
cookie = cookie & "AUTH_SESSION_ID_LEGACY=" & AUTH_SESSION_ID_LEGACY & ";" '& vbNewLine
cookie = cookie & "KC_RESTART=" & KC_RESTART
http.Open "GET", URL, False
http.SetRequestHeader "Cookie", cookie
http.SetRequestHeader "Connection", "keep-alive"
http.Option(6) = False 'выключаем редирект, чтобы получить куки от ABC
Call http.Send
http.waitForResponse 5
'location = http.GetResponseHeader("Location")
'получаем куки
KC_RESTART = Split(Split(http.GetAllResponseHeaders, "KC_RESTART=")(1), ";")(0)
ACTION = Split(Split(http.ResponseText, "ACTION=""")(1), """")(0)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 3 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "GET: " & URL)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Cookie: " & cookie)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 3 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_3_IDPGetCookie2 = "" & http.status
End Function
Private Function get_4_IDPPost1(Optional authUser As String = vbNullString) As String
Dim location As String
Dim http As Object
Dim cookie As String
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
URL = ACTION
cookie = ""
cookie = cookie & "AUTH_SESSION_ID=" & AUTH_SESSION_ID & ";" '& vbNewLine
cookie = cookie & "AUTH_SESSION_ID_LEGACY=" & AUTH_SESSION_ID_LEGACY & ";" '& vbNewLine
cookie = cookie & "KC_RESTART=" & KC_RESTART
http.Open "GET", URL, False
http.SetRequestHeader "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
http.SetRequestHeader "Content-type", "application/x-www-form-urlencoded"
http.SetRequestHeader "Cookie", cookie
http.SetRequestHeader "Connection", "keep-alive"
http.SetRequestHeader "Host", "idp.intra.net"
http.SetRequestHeader "User-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 YaBrowser/25.8.0.0 Safari/537.36"
Call http.Send
http.waitForResponse 5
session_code = Split(Split(http.ResponseText, "session_code=")(1), "&")(0)
execution = Split(Split(http.ResponseText, "execution=")(1), "&")(0)
tab_id = Split(Split(http.ResponseText, "tab_id=")(1), ">")(0)
ACTION = Split(Split(http.ResponseText, "action=""")(1), """")(0)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 4 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "GET: " & URL)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Cookie: " & cookie)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 4 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_4_IDPPost1 = "" & http.status
End Function
Private Function get_5_IDPPost2(authUser As String, authPass As String) As String
Dim URL As String
Dim params As String
Dim params_masked As String
Dim cookie As String
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
'случай, когда в url находятся закодированные символы, не воспринимаемые библиотекой WinHttp.WinHttpRequest.5.1
URL = Replace(ACTION, "&", "&")
params = ""
params = params & "username=" & authUser & "&"
params = params & "password=" & authPass & "&"
params = params & "credentialId="
params_masked = ""
params_masked = params_masked & "username=" & authUser & "&"
params_masked = params_masked & "password=*********" & "&"
params_masked = params_masked & "credentialId="
cookie = ""
cookie = cookie & "AUTH_SESSION_ID=" & AUTH_SESSION_ID & ";" '& vbNewLine
cookie = cookie & "AUTH_SESSION_ID_LEGACY=" & AUTH_SESSION_ID_LEGACY & ";" '& vbNewLine
cookie = cookie & "KC_RESTART=" & KC_RESTART
http.Open "POST", URL, False
http.SetRequestHeader "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
http.SetRequestHeader "Content-type", "application/x-www-form-urlencoded"
'длина очень важна
http.SetRequestHeader "Content-length", "" & 53
http.SetRequestHeader "Cookie", cookie
http.SetRequestHeader "Connection", "keep-alive"
http.SetRequestHeader "Host", "idp.intra.net"
http.SetRequestHeader "User-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 YaBrowser/25.8.0.0 Safari/537.36"
Call http.Send(params)
'http.waitForResponse 5
If http.status = 200 Then
IDP_IDENTITY = Split(Split(http.GetAllResponseHeaders, "IDP_IDENTITY=")(1), ";")(0)
IDP_IDENTITY_LEGACY = Split(Split(http.GetAllResponseHeaders, "IDP_IDENTITY_LEGACY=")(1), ";")(0)
IDP_SESSION = Split(Split(http.GetAllResponseHeaders, "IDP_SESSION=")(1), ";")(0)
IDP_SESSION_LEGACY = Split(Split(http.GetAllResponseHeaders, "IDP_SESSION_LEGACY=")(1), ";")(0)
ACTION = Split(Split(http.ResponseText, "ACTION=""")(1), """")(0)
code = Split(Split(http.ResponseText, "NAME=""code"" VALUE=""")(1), """")(0)
state = Split(Split(http.ResponseText, "NAME=""state"" VALUE=""")(1), """")(0)
session_state = Split(Split(http.ResponseText, "NAME=""session_state"" VALUE=""")(1), """")(0)
End If
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 5 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "POST: " & ACTION)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Content-type: application/x-www-form-urlencoded")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Content-length: 53")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Cookie: " & cookie)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", params_masked)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 5 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_5_IDPPost2 = "" & http.status
End Function
Private Function get_6_ABCAuth(Optional authUser As String = vbNullString) As String
Dim URL As String
Dim location As String
Dim params As String
Dim cookie As String
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
'https://ABC/app0/signin-oidc
URL = ACTION
params = ""
params = params & "code=" & code & "&"
params = params & "state=" & state & "&"
params = params & "session_state=" & session_state
'MsgBox params
cookie = ""
cookie = cookie & AspNetNonce & ";" '& vbNewLine
cookie = cookie & AspNetCorrelation
http.Open "POST", URL, False
'http.SetRequestHeader "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
http.SetRequestHeader "Content-type", "application/x-www-form-urlencoded"
'длина очень важна
http.SetRequestHeader "Content-length", "" & 691
'http.SetRequestHeader "Connection", "keep-alive"
http.SetRequestHeader "Cookie", cookie
'http.SetRequestHeader "Host", "ABC"
'http.SetRequestHeader "Origin", "null"
'http.SetRequestHeader "Pragma", "no-cache"
'http.SetRequestHeader "User-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 YaBrowser/25.8.0.0 Safari/537.36"
'очень важно. без этого не отдает код 302 и куки ABC
http.Option(6) = False
Call http.Send(params)
http.waitForResponse 60
MsgBox http.status
'MsgBox http.GetAllResponseHeaders
If http.status = 302 Then
ABC_Cookie = Split(Split(http.GetAllResponseHeaders, "ABC.WebApplication.Cookies=")(1), ";")(0)
Else
ABC_Cookie = vbNull
End If
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 6 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "POST: " & URL)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Content-type: application/x-www-form-urlencoded")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Content-length: 691")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Cookie: " & cookie)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", params)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 6 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_6_ABCAuth = "" & http.status
End Function
Михаил Левитин, Крупная российская организация
Язык VBA встроен во все офисные приложения, не смотря на свою кажущуюся устарелость, востребован при анализе информации, особенно числовой, финансовой. Это особенно востребовано в больших организациях, которые просто не успевают воплотить идеи работников по автоматизации их работы. Обычно, анализом занимаются подразделения, отвечающие за составление отчетности. Автоматизация их работы никогда не является приоритетом, не помогает даже RPA. Работникам, как и прежде, удобно воспользоваться Excel, Outlook для решения своих задач, а также автоматизировать рутинные операции с использованием языка VBA.
Перед тем, как анализировать информацию на VBA, ее необходимо собрать из разных источников. Самый простой механизм аутентификации – Basic. Он описан во множестве статей. Но в корпоративной экосистеме для аутентификации пользователей чаще применяют Kerberos аутентификацию, а в последнее время стала популярной аутентификация по протоколу OpenIdConnect (OIDC).
В статье рассмотрим методику анализа протокола аутентификации OpenIdConnect и самого популярного механизма авторизации – Authorization code flow, а также разработку приложения на языке VBA, осуществляющего за пользователя такую аутентификацию для последующего сбора данных из корпоративных систем. В статье есть код, который можно переиспользовать, но ввиду отличий в корпоративных инфраструктурах организаций, начать необходимо с анализа контекста организации, применяемых настроек протокола аутентификации.
Решая аналогичную проблему, у меня не получилось найти готовых решений ни на русском, ни на английском языке. Кроме Basic аутентификации на VBA, иной информации нет. Пришлось пройти путем экспериментов и потратить около 8 часов на изучение вопроса и воспроизведение протокола аутентификации OIDC на языке VBA. Надеюсь, информация в этой статье позволит сэкономить ваше время. Статья будет полезна как опытным разносторонним разработчикам с широким кругозором, а также начинающим специалистам по VBA без опыта web разработки. Те моменты, которые могут бы не интуитивны, особенно важны по тексту, заняли время на эксперименты, я выделю жирным. Сразу предупреждаю, что серебряной пули в статье нет, не проведя анализ вашего приложения, просто скопировать код и использовать не получится. Код потребует адаптации.
Анализируем протокол аутентификации
Возможности анализа в корпоративной инфраструктуре часто ограничены. Для анализа воспользуемся встроенными в браузер инструментами. В Chrome и Яндекс браузере - это функция «Исследовать элемент». В Edge – «Проверить». Дальнейшие названия буду приводить на примере Яндекс браузера.
Вначале, посмотрим на аутентификацию в нашем сервисе, назовем его ABC, глазами обычного пользователя. Открываем браузер в режиме инкогнито. Открываем сервис ABC. Протокол OpenIdConnect предполагает перенаправление пользователя для ввода пароля на страницу провайдера аутентификации, назовем его IDP. Эти два названия дальше будем часто использовать, стоит их запомнить (ABC, IDP). IDP предложит пользователю ввести логин и пароль. После ввода логина и пароля, браузер вернет нас обратно на страницу сервиса ABC, где мы увидим интересующие нас данные. Если сеть быстрая, то в большинстве случаем мы увидим 2 действия: страницу аутентификации IDP и страницу сервиса ABC с данными, но на самом деле, действий было гораздо больше. Часто, в корпоративной инфраструктуре используется Kerberos аутентификация, поэтому действий может быть 3. Браузер может дважды спросить пароль.
Подготовимся к анализу трафика. Опытным путем, самым удобным, в данном случае, способом упрощенно зафиксировать происходящее в сети является sequence диаграмма процесса. Хотя бы на бумаге нарисуйте 3х действующих лиц (браузер, ресурс ABC, IDP).
Попробуем разобраться, исследовать сетевой трафик. Закрываем браузер (надо обнулить куки), открываем его заново в режиме инкогнито и заходим на любой сайт, ждем его загрузки. Включаем режим исследования. Там, переключаемся на вкладку Network. Переходим страницу сервиса ABC, браузер опять отобразит страницу аутентификации IDP, но на вкладке Network появится множество строк, обозначающих перехваченные сетевые пакеты. Введем логин и пароль, дождемся загрузки ресурса ABC и остановим перехват трафика в браузере.
Проанализируем перехваченный трафик. Сетевых пакетов много, параметров в них тоже много. Не все пакеты нам интересны, не все параметры имеют отношение к аутентификации. Когда браузер пытается загрузить пользовательский интерфейс страницы ABC, то он скачает как саму страницу, так и различные картинки, css файлы, файлы javascript. Они нас не интересуют. Пойдем сверху вниз, пока не найдем следующие пакеты:
1. Вначале, когда браузер сделает запрос на сервис ABC, тот проверит, есть ли у пользователя токен для аутентификации в блоке cookie. Если токена нет, а в режиме инкогнито его действительно не будет, сервис ABC переправит пользователя на свою страницу аутентификации. Т.е. он может не сразу направить пользователя на IDP, а сначала направить на свой сервис аутентификации. URL такого сервиса может быть: ABC/login например. Т.е. уже на первом шаге есть опциональность, зависящая от конкретного сервиса, его настроек.
2. Сервис аутентификации ABC, если там настроен OpenIdConnect, перенаправит пользователя на IDP, передав в IDP конкретный адрес сервиса ABC, на который надо вернуть пользователя после прохождения аутентификации. Мы не всегда приходим на главную страницу сервиса ABC, у нас может быть ссылка на какой-то внутренний раздел. Смысл такого действия, чтобы пользователя вернуть на страницу, которую он запросил, а не домашнюю страницу сервиса ABC.
3. Часто, для удобства работы пользователей, корпоративные веб приложения используют Kerberos аутентификацию совместно с OpenIdConnect. Опять опциональность. Если она настроена, если тикетов в операционной системе нет, вы не в домене или в режиме инкогнито, то вверху в браузере вы увидите не типичное невзрачное окошко для ввода логина и пароля:
Cкриншот 1. Окно Kerberos аутентификации
В перехвате могут быть несколько сетевых пакетов связанных с попыткой Kerberos входа. Сложность анализа Kerberos в том, что браузер не видит некоторые Kerberos пакеты, т.к. браузер обращается к API операционной системы для прохождения Kerberos аутентификации (это еще один участник процесса). Мы только увидим, что браузер отправляет пакеты аутентификации, но не сможем понять откуда взялись данные у браузера для генерации этих пакетов. Для полноценного анализа Kerberos нужен снифер, но не каждому разрешат поставить его на рабочий компьютер и для достижения цели по OIDC нам это не очень нужно.
4. После попытки аутентификации в Kerberos, нас перекинет на форму аутентификации IDP. Отобразится обычные web форма аутентификации, основанная на теге form.
Cкриншот 2. Аутентификация через обычную web форму (тег form)
Воспроизводим OPENIDCONNECT на языке VBA, получаем тикет. Важные моменты
Весь вышеуказанный процесс необходимо воспроизвести шаг за шагом. Разработку удобно вести в отдельном модуле. Количество шагов на sequence диаграмме и в коде немного отличаются. Но если вы поняли смысл, это не должно доставить сложностей.
Для отладки лучше записывать данные сетевых пакетов в файл, потому самостоятельно. В код включены функции записи информации в файл (myFILE.CreateAfile, myFILE.AppendAFile), подготовьте эти функции самостоятельно, либо удалите их вызов из кода. Использование MsgBox и запись в ячейки EXCEL не удобно для отладки, сетевые пакеты могут быть очень большими.
В коде будет использована библиотека Microsoft WinHttp.WinHttpRequest.5.1.
Особое внимание нужно обратить на выключение редиректа - http.Option(6) = False. Если его не выключить, перенаправления произойдут не явно, вы не сможете прочитать важные параметры промежуточных пакетов и их Cookie, необходимые для дальнейших запросов.
Еще один важный момент – это проставление заголовка с длинной пакета. Если поленитесь его указать, можно потратить много времени на выяснения причины непредвиденных ответов сервера. Используем http.SetRequestHeader "Content-length", "" & ____.
И третий момент, с которым я столкнулся, проверяйте url на который ведет перенаправление. Там могут быть закодированные через апмперсант символы, кодирующие спец.знаки в строке url и не воспринимаемых библиотекой WinHttp.WinHttpRequest.5.1.
В остальном, надо просто воспроизвести то, что видим в перехвате трафика.
Представленный код не претендует на оптимальность, там могут быть не используемые переменные и последовательность отправки пакетов писалась для конкретного корпоративного сервиса.
Определяем переменные
Dim ABCCookies As String
Dim ACTION As String
Dim AUTH_SESSION_ID As String
Dim AUTH_SESSION_ID_LEGACY As String
Dim KC_RESTART As String
'куки получаемые от ABC сайта
Dim AspNetNonce As String
Dim AspNetCorrelation As String
Dim session_code As String
Dim execution As String
Dim tab_id As String
'куки IDP после успешной аутентификации
Dim IDP_IDENTITY As String
Dim IDP_IDENTITY_LEGACY As String
Dim IDP_SESSION As String
Dim IDP_SESSION_LEGACY As String
'код авторизации от IDP
Dim code As String
Dim state As String
Dim session_state As String
'искомый токен ABC
Dim ABC_Cookie As String
Dim ABC_Cookie_Period As String
Основная функция
Function get_ABC_Data(authUser As String, authPass As String, URL As String) As String
Dim ret As String
Dim r As String
myFILE.permitedWrite = True 'включаем логирование
r = myFILE.CreateAfile("C:\Users\USERNAME\Desktop\vba\log.txt", "Log ABC Auth")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
If ABC_Cookie = "" Then
ret = get_1_ABCCookie(URL) 'запрашиваем куки ABC и ссылку на провайдер аутентификации
ret = get_2_IDPGetCookie1 'get запрос аутентификации, получаем куки idp
ret = get_3_IDPGetCookie2 'повторно
ret = get_4_IDPPost1 'получаем форму ввода пароля
ret = get_5_IDPPost2(authUser, authPass) 'получаем код авторизации, который на сл.шаге меняем на токен
ret = get_6_ABCAuth 'получаем токен от ABC
End If
myFILE.permitedWrite = False 'выключаем логирование
get_ABC_Data = ""
End Function
Функции вызова конкретных пакетов flow
Private Function get_1_ABCCookie(URL As String) As String
Dim location As String
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
http.Open "GET", URL, False
http.SetRequestHeader "Connection", "keep-alive"
http.Option(6) = False 'выключаем редирект, чтобы получить куки от ABC
Call http.Send
http.waitForResponse 5
'получаем куки
AspNetNonce = ".AspNetCore.OpenIdConnect.Nonce." & Split(Split(http.GetAllResponseHeaders, ".AspNetCore.OpenIdConnect.Nonce.")(1), ";")(0)
AspNetCorrelation = ".AspNetCore.Correlation." & Split(Split(http.GetAllResponseHeaders, ".AspNetCore.Correlation.")(1), ";")(0)
'получаем адрес перенаправления для следующего запроса
ACTION = http.GetResponseHeader("Location")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 1 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "GET: " & URL)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 1 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_1_ABCCookie = "" & http.status
End Function
Private Function get_2_IDPGetCookie1(Optional authUser As String = vbNullString) As String
Dim location As String
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
URL = ACTION
http.Open "GET", URL, False
http.Option(6) = False 'выключаем редирект, чтобы получить куки от ABC
http.SetRequestHeader "Connection", "keep-alive"
Call http.Send
http.waitForResponse 5
'location = http.GetResponseHeader("Location")
'получаем куки
AUTH_SESSION_ID = Split(Split(http.GetAllResponseHeaders, "AUTH_SESSION_ID=")(1), ";")(0)
AUTH_SESSION_ID_LEGACY = Split(Split(http.GetAllResponseHeaders, "AUTH_SESSION_ID_LEGACY=")(1), ";")(0)
KC_RESTART = Split(Split(http.GetAllResponseHeaders, "KC_RESTART=")(1), ";")(0)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 2 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "GET: " & URL)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 2 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_2_IDPGetCookie1 = "" & http.status
End Function
Private Function get_3_IDPGetCookie2(Optional authUser As String = vbNullString) As String
Dim location As String
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
URL = ACTION
cookie = ""
cookie = cookie & "AUTH_SESSION_ID=" & AUTH_SESSION_ID & ";" '& vbNewLine
cookie = cookie & "AUTH_SESSION_ID_LEGACY=" & AUTH_SESSION_ID_LEGACY & ";" '& vbNewLine
cookie = cookie & "KC_RESTART=" & KC_RESTART
http.Open "GET", URL, False
http.SetRequestHeader "Cookie", cookie
http.SetRequestHeader "Connection", "keep-alive"
http.Option(6) = False 'выключаем редирект, чтобы получить куки от ABC
Call http.Send
http.waitForResponse 5
'location = http.GetResponseHeader("Location")
'получаем куки
KC_RESTART = Split(Split(http.GetAllResponseHeaders, "KC_RESTART=")(1), ";")(0)
ACTION = Split(Split(http.ResponseText, "ACTION=""")(1), """")(0)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 3 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "GET: " & URL)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Cookie: " & cookie)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 3 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_3_IDPGetCookie2 = "" & http.status
End Function
Private Function get_4_IDPPost1(Optional authUser As String = vbNullString) As String
Dim location As String
Dim http As Object
Dim cookie As String
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
URL = ACTION
cookie = ""
cookie = cookie & "AUTH_SESSION_ID=" & AUTH_SESSION_ID & ";" '& vbNewLine
cookie = cookie & "AUTH_SESSION_ID_LEGACY=" & AUTH_SESSION_ID_LEGACY & ";" '& vbNewLine
cookie = cookie & "KC_RESTART=" & KC_RESTART
http.Open "GET", URL, False
http.SetRequestHeader "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
http.SetRequestHeader "Content-type", "application/x-www-form-urlencoded"
http.SetRequestHeader "Cookie", cookie
http.SetRequestHeader "Connection", "keep-alive"
http.SetRequestHeader "Host", "idp.intra.net"
http.SetRequestHeader "User-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 YaBrowser/25.8.0.0 Safari/537.36"
Call http.Send
http.waitForResponse 5
session_code = Split(Split(http.ResponseText, "session_code=")(1), "&")(0)
execution = Split(Split(http.ResponseText, "execution=")(1), "&")(0)
tab_id = Split(Split(http.ResponseText, "tab_id=")(1), ">")(0)
ACTION = Split(Split(http.ResponseText, "action=""")(1), """")(0)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 4 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "GET: " & URL)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Cookie: " & cookie)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 4 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_4_IDPPost1 = "" & http.status
End Function
Private Function get_5_IDPPost2(authUser As String, authPass As String) As String
Dim URL As String
Dim params As String
Dim params_masked As String
Dim cookie As String
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
'случай, когда в url находятся закодированные символы, не воспринимаемые библиотекой WinHttp.WinHttpRequest.5.1
URL = Replace(ACTION, "&", "&")
params = ""
params = params & "username=" & authUser & "&"
params = params & "password=" & authPass & "&"
params = params & "credentialId="
params_masked = ""
params_masked = params_masked & "username=" & authUser & "&"
params_masked = params_masked & "password=*********" & "&"
params_masked = params_masked & "credentialId="
cookie = ""
cookie = cookie & "AUTH_SESSION_ID=" & AUTH_SESSION_ID & ";" '& vbNewLine
cookie = cookie & "AUTH_SESSION_ID_LEGACY=" & AUTH_SESSION_ID_LEGACY & ";" '& vbNewLine
cookie = cookie & "KC_RESTART=" & KC_RESTART
http.Open "POST", URL, False
http.SetRequestHeader "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
http.SetRequestHeader "Content-type", "application/x-www-form-urlencoded"
'длина очень важна
http.SetRequestHeader "Content-length", "" & 53
http.SetRequestHeader "Cookie", cookie
http.SetRequestHeader "Connection", "keep-alive"
http.SetRequestHeader "Host", "idp.intra.net"
http.SetRequestHeader "User-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 YaBrowser/25.8.0.0 Safari/537.36"
Call http.Send(params)
'http.waitForResponse 5
If http.status = 200 Then
IDP_IDENTITY = Split(Split(http.GetAllResponseHeaders, "IDP_IDENTITY=")(1), ";")(0)
IDP_IDENTITY_LEGACY = Split(Split(http.GetAllResponseHeaders, "IDP_IDENTITY_LEGACY=")(1), ";")(0)
IDP_SESSION = Split(Split(http.GetAllResponseHeaders, "IDP_SESSION=")(1), ";")(0)
IDP_SESSION_LEGACY = Split(Split(http.GetAllResponseHeaders, "IDP_SESSION_LEGACY=")(1), ";")(0)
ACTION = Split(Split(http.ResponseText, "ACTION=""")(1), """")(0)
code = Split(Split(http.ResponseText, "NAME=""code"" VALUE=""")(1), """")(0)
state = Split(Split(http.ResponseText, "NAME=""state"" VALUE=""")(1), """")(0)
session_state = Split(Split(http.ResponseText, "NAME=""session_state"" VALUE=""")(1), """")(0)
End If
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 5 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "POST: " & ACTION)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Content-type: application/x-www-form-urlencoded")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Content-length: 53")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Cookie: " & cookie)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", params_masked)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 5 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_5_IDPPost2 = "" & http.status
End Function
Private Function get_6_ABCAuth(Optional authUser As String = vbNullString) As String
Dim URL As String
Dim location As String
Dim params As String
Dim cookie As String
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
'https://ABC/app0/signin-oidc
URL = ACTION
params = ""
params = params & "code=" & code & "&"
params = params & "state=" & state & "&"
params = params & "session_state=" & session_state
'MsgBox params
cookie = ""
cookie = cookie & AspNetNonce & ";" '& vbNewLine
cookie = cookie & AspNetCorrelation
http.Open "POST", URL, False
'http.SetRequestHeader "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
http.SetRequestHeader "Content-type", "application/x-www-form-urlencoded"
'длина очень важна
http.SetRequestHeader "Content-length", "" & 691
'http.SetRequestHeader "Connection", "keep-alive"
http.SetRequestHeader "Cookie", cookie
'http.SetRequestHeader "Host", "ABC"
'http.SetRequestHeader "Origin", "null"
'http.SetRequestHeader "Pragma", "no-cache"
'http.SetRequestHeader "User-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 YaBrowser/25.8.0.0 Safari/537.36"
'очень важно. без этого не отдает код 302 и куки ABC
http.Option(6) = False
Call http.Send(params)
http.waitForResponse 60
MsgBox http.status
'MsgBox http.GetAllResponseHeaders
If http.status = 302 Then
ABC_Cookie = Split(Split(http.GetAllResponseHeaders, "ABC.WebApplication.Cookies=")(1), ";")(0)
Else
ABC_Cookie = vbNull
End If
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "" & Now)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- REQUEST 6 ---------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "POST: " & URL)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Connection: keep-alive")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Content-type: application/x-www-form-urlencoded")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Content-length: 691")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Cookie: " & cookie)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", params)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "------------------------- RESPONSE 6 --------------------------")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "Status: " & http.status & " " & http.statusText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "HEADERS")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.GetAllResponseHeaders)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "PAYLOAD")
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", http.ResponseText)
r = myFILE.AppendAFile("C:\Users\USERNAME\Desktop\vba\log.txt", "")
get_6_ABCAuth = "" & http.status
End Function
Михаил Левитин, Крупная российская организация