← Назад в базу знаний

Как антифрод Stripe выявляет прокси по TLS JA3/JA4 отпечатку при оплате картой

Как антифрод Stripe выявляет прокси по TLS JA3/JA4 отпечатку при оплате картой

Что вообще видит Stripe на TLS-уровне

Когда браузер клиента идёт на `api.stripe.com` или на хостнутую страницу оплаты, первым делом поднимается TLS-соединение. До того как улетит хоть один HTTP-заголовок, до того как JavaScript успеет что-то собрать, Stripe уже получает ClientHello. Это первое сообщение рукопожатия содержит кучу метаданных: версию TLS, список поддерживаемых шифров, набор расширений, эллиптические кривые, форматы точек, ALPN-протоколы, длины всего перечисленного.

Из этого набора считается хеш — JA3 (MD5 от строки с шифрами, расширениями, кривыми и т.д.) или его более новый вариант JA4. Хеш стабилен для конкретной реализации TLS-стека. Chrome 120 с определённым набором флагов даёт один JA3, Firefox 121 — другой, curl 8.5 — третий, Go-клиент — четвёртый. Python `requests` с urllib3 — вообще отдельная история.

Антифрод Stripe (Radar) собирает эти отпечатки и сопоставляет с тем, что заявляет клиент. Если User-Agent говорит «Chrome 120 на Windows», а JA3 соответствует curl или Go — это сигнал. Не приговор, но сигнал, который складывается с десятком других.

Почему обычный прокси палится сразу

Прокси сам по себе не меняет TLS-рукопожатие. SOCKS5-прокси просто туннелирует TCP-поток, TLS-сессия поднимается между клиентом и Stripe напрямую. Значит JA3 остаётся клиентским. Это нормально, пока клиент — реальный браузер.

Проблема начинается, когда через прокси гонят автоматизацию. Selenium с headless Chrome даёт JA3, отличный от обычного Chrome — из-за отличий в сборке BoringSSL и набора включённых расширений. Puppeteer с `--disable-blink-features` тоже меняет картину. А если это `curl` или `python-requests` через HTTP-прокси — JA3 будет вообще не браузерным.

Stripe видит: с одного IP идёт поток платежей, у которых JA3 = python-requests/2.31. При этом User-Agent подделан под Chrome. Расхождение между заявленным и реальным стеком — классический маркер фрода.

Как выглядит JA3 на практике

Возьму реальный пример. Chrome 120 на Linux:

```

771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0

```

JA3 = `cd08e31494f9531f560d64c695473da9`. Это MD5 от строки выше.

curl 8.5 (OpenSSL 3.2):

```

771,4866-4867-4865-49196-49200-49195-49199-52393-52392-49188-49192-49162-49172-49187-49191-49161-49171-52394-52393,0-11-10-35-22-23-13-43-45-51-21,29-23-30-25-24,0-1-2

```

JA3 = `1f6c3b6f...` — совсем другой хеш.

Python requests с urllib3 2.x через OpenSSL:

```

771,49195-49199-49196-49200-52393-52392-49161-49171-49162-49172-156-157-47-53,0-11-10-35-13-43-45-51,29-23-24-25,0

```

JA3 = `3b5074b1b5d032e5620f69f9f700ff0e`. Ещё один вариант.

Stripe держит базу таких отпечатков. Совпадение с известным браузерным JA3 — плюс к доверию. Совпадение с библиотечным — минус.

JA4 — что изменилось

JA4 появился в 2023 как ответ на слабости JA3. Основные отличия:

- вместо MD5 используется SHA-256 с усечением;

- учитывается порядок расширений и их сортировка;

- отдельно кодируется ALPN и SNI;

- есть JA4H (HTTP), JA4S (server), JA4X (X.509).

Формат JA4: `t13d1516h2_8daaf6152771_b186095e22b6`. Первая часть — версия TLS, тип шифра, количество расширений, ALPN. Вторая — хеш шифров. Третья — хеш расширений.

Для антифрода JA4 удобнее тем, что позволяет отличать не только стек, но и версию библиотеки. Chrome 120 и Chrome 121 могут дать разные JA4 при одинаковом JA3. Это даёт более точную привязку.

Что делает прокси-провайдер

Тут начинается интересное. Прокси, который просто перекидывает TCP, ничего не меняет в JA3. Прокси с TLS-терминацией (MITM) подменяет отпечаток на свой — и это обычно хуже, потому что сертификат придётся подсовывать клиенту, а Stripe видит сертификат прокси-ЦС.

Качественные прокси-сервисы для скрейпинга и автоматизации сейчас поднимают JA3-мимикрию. Это когда клиент не сам устанавливает TLS, а отдаёт запрос прокси, а прокси поднимает соединение с нужным отпечатком. Библиотеки вроде `curl-impersonate`, `tls-client` (Go), `cycleTLS` умеют воспроизводить JA3 Chrome или Firefox на уровне рукопожатия.

Схема выглядит так:

```

client -> proxy (JA3 spoof) -> Stripe

```

Клиент отправляет HTTP-запрос в открытом виде (или через CONNECT), прокси сам делает TLS-рукопожатие с отпечатком, идентичным Chrome 120. Stripe видит JA3 Chrome и не может отличить от настоящего браузера по этому параметру.

На практике с IPv6-прокси вроде тех, что крутятся через lexic.ml, это работает так: клиент идёт на прокси по IPv6, прокси делает исходящее соединение с подставленным JA3. Для Stripe источник — IPv6-адрес из пула, отпечаток — браузерный.

Пример: проверка собственного JA3

Чтобы понять, что видит Stripe, надо сначала посмотреть на свой отпечаток. Простой способ — через публичные эндпоинты:

```bash

curl -s https://tls.peet.ws/api/all | jq '.tls.ja3_hash, .tls.ja4'

```

Вывод покажет ваш JA3 и JA4. Если запустить это из Python:

```python

import requests

r = requests.get("https://tls.peet.ws/api/all", timeout=10)

data = r.json()

print("JA3:", data["tls"]["ja3_hash"])

print("JA4:", data["tls"]["ja4"])

print("User-Agent:", data["http_version"])

```

Сравните с тем, что выдаёт браузер. Если разница есть — Stripe её тоже увидит.

Почему одного JA3 недостаточно

Stripe Radar — это не только TLS-фингерпринт. Это совокупность:

- JA3/JA4;

- HTTP/2 fingerprint (SETTINGS-фрейм, порядок заголовков, приоритеты);

- TCP/IP fingerprint (TTL, window size, MSS, опции);

- canvas/WebGL fingerprint из JS;

- поведенческие метрики (скорость заполнения формы, движение мыши);

- история IP (сколько аккаунтов с него платило, были ли чарджбеки).

JA3 — один из сигналов. Если он совпадает с браузерным, но HTTP/2-отпечаток библиотечный — палево. Если оба совпадают, но canvas пустой — тоже. Если всё совпадает, но IP числится в датацентровом диапазоне — вопрос.

Именно поэтому прокси с одной только JA3-мимикрией не спасает. Нужна полная эмуляция стека.

HTTP/2 fingerprint — следующий слой

После TLS идёт HTTP/2. Клиент отправляет SETTINGS-фрейм с параметрами: `HEADER_TABLE_SIZE`, `ENABLE_PUSH`, `MAX_CONCURRENT_STREAMS`, `INITIAL_WINDOW_SIZE`, `MAX_FRAME_SIZE`, `MAX_HEADER_LIST_SIZE`. Порядок и значения этих параметров уникальны для реализации.

Chrome отправляет:

```

SETTINGS: 1:65536, 2:0, 4:6291456, 6:262144

WINDOW_UPDATE: 15663105

PRIORITY: stream 3, weight 200, dep 0

```

curl отправляет:

```

SETTINGS: 3:100, 4:1073741824

WINDOW_UPDATE: 1073741824

```

Разница видна сразу. Stripe собирает HTTP/2 fingerprint так же, как JA3 — хеширует параметры. Несоответствие между JA3 (Chrome) и HTTP/2 (curl) — красный флаг.

Как это выглядит в логах Stripe

Точных логов Stripe никто не публикует, но по косвенным данным и по документации Radar можно восстановить картину. При каждой оплате формируется риск-скор. В него входят:

| Параметр | Вес (условно) | Что проверяется |

|---|---|---|

| JA3/JA4 | средний | совпадение с заявленным UA |

| HTTP/2 fingerprint | средний | согласованность с TLS |

| IP-репутация | высокий | датацентр, история, ASN |

| Canvas/WebGL | высокий | наличие, энтропия |

| Поведение | высокий | тайминги, движения |

| 3DS | высокий | прохождение аутентификации |

Если JA3 палит, но всё остальное чисто — скорее всего пройдёт. Если JA3 чистый, но IP датацентровый и canvas пустой — блок.

Что делать, если нужна автоматизация

Тут два пути. Первый — полная эмуляция браузера через Playwright с реальным Chrome, а не headless. Это дорого по ресурсам, но даёт настоящие отпечатки. Второй — TLS-мимикрия плюс HTTP/2-мимикрия плюс резидентные IP.

Пример на Python с `tls-client`:

```python

from tls_client import Session

session = Session(client_identifier="chrome_120")

response = session.get(

"https://api.stripe.com/v1/tokens",

headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."}

)

print(response.status_code)

```

`client_identifier="chrome_120"` подставляет JA3, JA4 и HTTP/2-параметры Chrome 120. Это уже не просто прокси, а полноценная мимикрия.

Но даже это не гарантия. Stripe смотрит на IP. Резидентный IPv4 с историей — ок. Датацентровый IPv6 — подозрительно. Хотя IPv6 из residential-пулов сейчас набирает вес.

Почему IPv6 меняет расклад

IPv6-адресов много. Пул /64 — это 18 квинтиллионов адресов. Прокси-провайдеры выдают клиентам адреса из своих /48 или /56, и каждый адрес выглядит как отдельный хост. Stripe не может просто забанить диапазон — это отрежет легитимных пользователей.

Но у IPv6 есть особенность: многие датацентры анонсируют крупные блоки, и ASN сразу выдаёт принадлежность. Если JA3 идеальный, а IP из ASN хостера — риск-скор растёт. Если IP из residential-пула — падает.

Комбинация «браузерный JA3 + резидентный IPv6 + чистый HTTP/2» — это то, что сейчас проходит. Но гонка продолжается: Stripe обновляет детекторы, прокси-провайдеры обновляют мимикрию.

Ошибки, которые убивают анонимность

Типичные грабли. Первая — использовать `requests` с подменой User-Agent. JA3 выдаст Python с потрохами. Вторая — гонять через HTTP-прокси без TLS-мимикрии. Прокси видит открытый CONNECT, а Stripe видит клиентский JA3. Третья — смешивать отпечатки: JA3 от Chrome, HTTP/2 от Firefox. Детектор ловит несоответствие.

Ещё одна — IPv6-адрес с обратным DNS, указывающим на хостинг. Stripe резолвит PTR и видит `hosted-by-datacenter.example`. Всё, минус к доверию.

И последняя — не менять отпечаток между сессиями. Если с одного IP идёт 500 платежей с одинаковым JA3 и одинаковым HTTP/2 — это бот. Настоящий браузер варьирует хотя бы версии расширений.

Итог

JA3 и JA4 — это первые слои детекта, но не единственные. Stripe собирает отпечатки на TLS, HTTP/2, TCP и JS-уровнях, сопоставляет их между собой и с заявленным клиентом. Прокси без мимикрии палится на первом же рукопожатии. Прокси с мимикрией проходит TLS, но может спалиться на HTTP/2 или IP-репутации.

Рабочая схема сегодня — это полный стек: браузерный JA3/JA4, согласованный HTTP/2 fingerprint, резидентный IPv6 из чистого ASN, реальный canvas и поведенческие метрики. Всё, что короче, рано или поздно упирается в блок.

✔️Купить прокси