Как Яндекс.Карты защищаются от парсинга
До недавнего времени Яндекс.Карты никак не ограничивали запросы к непубличному API карт. Их можно было парсить в несколько потоков с одного IP адреса в течение недели и не словить бан.
Теперь же Яндекс стал подписывать каждый запрос. А это значит, что передать свои параметры в запрос без генерации новой подписи невозможно.
Подпись создается на стороне клиента, а значит найти алгоритм генерации это вопрос времени.
Для этого открываем maps.yandex.ru в Chrome, затем Dev Tools -> Network -> XHR. Кликаем на любой объект на карте, и во кладке с запросами наводим мышь на колонку Initiator у любого запроса с подписью.
Откроется такая цепочка вызовов:
Осталось найти в ней код, который генерирует подпись. Для этого сразу кликаем на вторую строчку и видим следующее:
Под цифрой 3 отмечена функция, которая генерирует подпись. Давайте ее разберем:
В качестве единственного аргумента функция принимает объект с параметрами будущего запроса.
После этого объект сортируется по ключу в порядке возрастания и преобразовывается в строку. Результат присваивается переменной t.
Последний этап это генерация подписи. За это отвечает странный код в цикле for, который является ничем иным как JS реализацией популярного хэш алгоритма djb2.
Алгоритм делает следующее:
- Задает изначальное значение хэша равным 5381. Почему именно это число? Потому что в результате тестов удалось выяснить, что так хэш-функция меньше подвержена коллизиям и обладает хорошим лавинным эффектом.
- Перебирает символы в строке с параметрами и умножает 33 на текущее значение хэша, преобразуя его в 32 битное благодаря оператору XOR. Почему именно 33? Никто точно не может объяснить. Возможно это связано с тем, что 33 легко заменить на побитовую операцию n << 5, которая выполняется процессором быстрее обычного умножения.
- Возвращает хэш, попутно еще раз преобразовав его в 32 битное число.
Затем полученная подпись добавляется к остальным параметрам и отправляется на сервер, где проверяется по такому же алгоритму, как и генерируется.
Алгоритм разобран, теперь проверим его. Для упрощения мы минуем реализацию сортировки параметров и сразу перейдем к генерации подписи.
Возьмем оригинальный запрос Яндекс, как пример того, что должно получиться:
https://yandex.ru/maps/api//tycoon/fetchAuthorizedBusinesses?ajax=1&csrfToken=b0c6443bcacfe78152b8dc3ca49f4754e1b0e88d%3A1626510632&s=3267941894&sessionId=1636510632632_659280
Параметр s равен 3267941894. Значит при генерации подписи для строки ajax=1&csrfToken=b0c6443bcacfe78152b8dc3ca49f4754e1b0e88d%3A1626510632&sessionId=1636510632632_659280 должно получиться именно это число.
В итоге мы выяснили, что подпись запросов не помогает защититься от парсинга, так как все вычисления происходят на стороне клиента. Скорее всего в Яндексе это понимают, и единственная причина по которой они ввели подписи – сломать все работавшие до этого момента парсеры