@atoster

Каким образом защищенный сайт допускает запросы «живых» пользователей, но блокирует запросы Python Requests и аналогов и как это обойти?

Есть сайт, к которому пробую подключиться на питоне через Requests.

На сайте есть очень серьезная защита от загрузки страниц с помощью внешних программ и без длинного описания здесь не обойтись.

Если на сайт заходит человек, используя браузер, то никаких проблем не возникает. В этом случае сайт открывается без каких-либо видимых нюансов.

Но если подключиться к этому сайту с помощью Requests, то даже "правильную" главную страницу невозможно получить. Вместо нормальной главной страницы возвращается служебная страница, содержащая встроенные javascript'ы, прописанные таким образом, что крайне трудно понять, что именно они делают и как их обойти, а также в служебной странице сохранен RSA ключ шифрования для каких-то хитрых манипуляций.

HTML код служебной страницы выложил на Codepen. (В двух местах там IP и временную метку заменил).
https://codepen.io/atoster/pen/ExQwONd?editors=1000


Теперь детали, - как я понимаю отдельные важные моменты и что примерно происходит.

В заглавной части служебной страницы загружаются два внешних javascript.
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jsrsasign/8.0.20/jsrsasign-all-min.js"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.js"></script>


Первый из скриптов нужен для какого-то шифрования или дешифрирования. Второй - составляет уникальный Fingerprint браузера.

Когда пользователь открывает страницу, автоматически загружаются скрипты, которые вычисляют уникальный отпечаток (Fingerprint) браузера, задаются куки spsn (числовой, на основании какой-то unix-временной метки), "spid" и "spsc". Последние две задаются с помощью шифрования и дешифрирования какой-то информации, вероятно, отпечатка.

Дальше управление передается функции "get_location()", которая делает переадресацию, подставляя в url адрес разные рассчитанные хеши, IP итп. Переадресация идет на какой-то служебный адрес, начинающийся с "/xpvnsulc/". В результате после проверки у обычного живого пользователя сайт либо автоматически открывается, либо отображается промежуточная страница с уведомлением о большой нагрузке на сервер и просьбе ввести символы с каптчи (картинки).

После ввода символов такая же просьба может повториться еще один или несколько раз. Дальше происходит переход на сайт. Сама функция на служебной странице, которая делает все эти манипуляции, путает в алгоритме следы, называния переменных итп - все сделано для того, чтобы невозможно было понять алгоритм. ... А "/xpvnsulc/" - если набрать в поисковике, то находятся 10-20 результатов. Т.е. такая проверка каптчи и на других сайтах встречается, но не часто.

Надо заметить, что увидеть промежуточную служебную страницу в браузере тоже непросто. Джаваскипты при загрузке страницы запускаются и делают все очень быстро, - в панели веб-разработчика её не видно. В браузере в режиме веб-разработчика на вкладке Network видна уже итоговая html страница с полезной информацией, в html коде которой ничего из вышеописанного нет.

Чтобы увидеть эту "служебную страницу", я через hosts файл запретил доступ к двум внешним джаваскриптам. Если они не стартуют, то тогда служебная страница видна - кроме нее ничего не загружается.
127.0.0.1 cdnjs.cloudflare.com
127.0.0.1 cdn.jsdelivr.net

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

Отсюда возникают сложности.
Подключиться к сайту и прочитать полезный контент через Requests не получается. Я пробовал имитировать куки-файлы, поля referer, host, origin - не помогает.

Единственное, что получилось сделать - это подключиться к сайту и получить "правильный" контент через библиотеку Selenium. Selenium открывает страницу в реальном браузере. При заходе на сайт через Selenium автоматически запускаются все скрипты. Поскольку от человека поведение не отличается (почти), защита не срабатывает и контент доступен.

Но Selenium очень медленный и не все умеет, что требуется. Мне нужно подключиться через Python Requests или что-то похожее.
В поисках решения я использовал библиотеку Requestium (для ее работы нужна версия Selenium ниже 4.0).
Requestium при подключении позволяет создать сессию и одновременно управлять ей, как с помощью Selenium, так и с помощью аналога Requests.

С помощью Requestium я запускаю Selenium и открываю главную страницу сайта - все проверки сайта проходят и защита не блокирует доступ.
На этом этапе в драйвере Selenium (в управляемом им браузере) сформированы все куки и все, что нужно для просмотра любых страниц сайта.

Далее я использую функцию Requestium - session.transfer_driver_cookies_to_session(), которая все куки из браузера автоматически копирует в куки сессии Requests.
Дополнительно я также копирую из браузера в сессию requests название user-agent - session.copy_user_agent_from_driver().
Перед этим я заранее приготовил заголовки headers, в которых прописан referrer, host и origin.
Прежде чем делать запрос, я проверяю, что в сессию Requests действительно скопировались все нужные куки из браузера - print (session.cookies).

Дальше я пытаюсь получить страницу через ~requests (requestium):
r = session.post(url, data=payload, headers=headers)


Но сколько бы я не пробовал сымитировать браузер с помощью ~Requests, нормальный контент не отдается, а всегда возвращается хоть и с кодом 200, но служебная страница "защиты".
В примере выше указан пример с POST запросом, поскольку конечная цель обращения к сайту через POST. Но я пробовал и с GET запросами, на любые страницы, включая главную - результат одинаковый. Поэтому я думаю, что если получить доступ к главной странице, то к остальным страницам алгоритм подключения будет аналогичный.

К другим сайтам я много раз подключался через Requests и не сталкивался с нерешаемыми препятствиями. А здесь никак не получается разгадать механизм защиты. Я уже голову сломал, что именно может блокировать доступ к сайту питоновским скриптам и на чем основана проверка. Прошу помочь с идеями.

P.S. Адрес сайта указан в коде в функции get_location(). Результат планировалось использовать локально и исключительно в благих целях, - для выгрузки данных по отдельному населенному пункту для поиска родственников и "забытых" земляков-героев.
  • Вопрос задан
  • 2633 просмотра
Пригласить эксперта
Ответы на вопрос 3
@morunas
Столкнулся с такой же проблемой на том же сайте. При открытии через Python+requests получаю мусорную страницу размером 31 Кб вместо 700 Кб. Тоже самое через библиотеки socket+ssl и httpx.
Открываю в браузере (можно даже fetch запросом, без JavaScript и cookie) нормально. Через Python+Selenium тоже без проблем, но для моей задачи он избыточен.
Дело точно не в HTTP-заголовках, подставлял точно такие как в браузере (с регистром, переносами и пробелами). Нагуглил про TLS Fingerprint, JA3 отпечатки и т.п, до конца не разобрался, но думаю проверка именно на уровне TLS, решение оказалось простым - библиотека github.com/yifeikong/curl_cffi
Пример
from curl_cffi import requests
session = requests.Session()
response = session.get('https://example.com', impersonate="chrome110")
print(f'Status Code: {response.status_code}, Content size: {len(response.text)}')
print (response.text)
Ответ написан
Комментировать
seven5674
@seven5674
Старый я уже что бы что-то в себе менять
ох и сколько же у тебя еще интересных открытий в жизни впереди...

p.s. отвечали на твой вопрос уже наверное раз миллион. ищи поиском
Ответ написан
Комментировать
@rPman
Не дам готовый ответ но посоветую - открываешь отладчик в браузере по F12, вкладку network, проводишь полный сеанс работы пользователя с учетом чистых кук (и отдельно повторный заход), делаешь экспорт .par файла (это json файл, там все запросы, и ответы, полная информация)

Затем смотришь нужные тебе запросы, они могут требовать (в заголовках или в параметрах) некие данные, которые собственно обфусицированным скриптом сайт и генерирует в 99% случаев это не скрытая строка а напрямую запрашиваемая в предыдущих запросах, т.е. ее можно поискать в par файле (лучше не как текст а разбирай json так как там могут быть строки с ковычками). Вместо ручного анализа может оказаться проще писать небольшой скрипт ковыряющийся в этих логах и доступных html/js файлах сайта.

По уму отладчик в браузере умеет искать по истории запросов (там где есть поиск по url есть кнопка лупа - поиск, ее нажатие открывает еще одно поле поиска уже по данным)

Работа с логом очень помогает, но сочувствую тебе.

Деобфусикация и реверсинженеринг кода веб сайта стоит 10х-100х от такой же но с использоанием selenium или инжекта кода на страницу (например отдельное самописное приложение с браузером), сочувствую тебе. Подумай еще раз хорошенько, так как через условный год этот сайт так же придется разбирать и анализировать за те же деньго-трудо-затраты.

p.s. для тех кто пишет защиту от парсинга - совет, увеличивайте нагрузку на процессор (в т.ч. используя gpu) чтобы всякие selenium потребовали ну очень много ресурсов (грубо говоря фиктивную задачу как в майнинге либо 100500-уровневая раз дешифрация или хеширование)... подбирая такие нагрузки, чтобы рядовой клиент все еще не замечал проблем а вот массовый анализ стал бы затратным.
Такова селяви.. либо делай данные общими либо создавай проблемы всем.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы