使用 TLS 指纹识别检测和绕过应用指纹

使用 TLS 指纹识别检测和绕过应用指纹

📚 前置知识

本配方涉及以下核心技术,建议先阅读相关章节:

问题场景

你遇到了什么问题?

  • 你的自动化脚本被服务器识别并封禁了
  • 你用 Python/curl 请求 API,但服务器返回 403/风控拦截
  • 你想伪装成真实浏览器/官方 App 的 TLS 指纹
  • 你想分析 App 使用的 TLS 库和配置
  • 你想检测自己的请求是否暴露了异常的 TLS 特征

本配方教你:理解 TLS 指纹识别原理、如何检测自己的 TLS 指纹、以及如何伪造合法的 TLS 指纹。

核心理念

💡 TLS 指纹是应用的"DNA"

  • TLS 握手阶段暴露了客户端使用的库和配置
  • 不同的 HTTP 客户端有不同的 TLS 指纹
  • 服务器可以通过 JA3/JA4 指纹识别你的真实身份
  • 即使使用 HTTPS,TLS 握手特征也是明文的

预计用时: 20-40 分钟


工具清单

必需工具

可选工具

    • curl-impersonate - 伪装浏览器 TLS 指纹的 curl
    • tls-client (Python) - 支持自定义 TLS 指纹的 HTTP 库
    • Burp Suite - 抓包分析
    • ja3transport (Go) - Go 语言的 TLS 伪装库

前置条件

✅ 确认清单

  1. Wireshark 已安装并可用
  2. Python 3.7+ 环境配置完成
1
2
3
4
5
6
7
8
# 验证 Wireshark 安装
wireshark --version

# 验证 Python 环境
python3 --version

# 安装必要的 Python 库
pip3 install requests pycurl tls-client

解决方案

第 1 步:理解 TLS 指纹识别原理(5 分钟)

1.1 什么是 JA3 指纹?

JA3 是一种通过分析 TLS Client Hello 包生成指纹的技术。

提取的字段

  1. TLS 版本(如 TLS 1.3 = 771)
  2. 加密套件列表(Cipher Suites)
  3. 扩展列表(Extensions)
  4. 椭圆曲线列表(Elliptic Curves)
  5. 椭圆曲线点格式(EC Point Formats)

生成过程

1
2
3
4
5
拼接成String: "771,4865-4866-4867,0-23-65281,29-23-24,0"
Calculate MD5 哈希
JA3 指纹: e7d705a3286e19ea42f587b344ee6865
特性MD5 指纹JA3 指纹
格式MD5 哈希结构化字符串
可读性高(包含版本、计数等)
示例e7d705a3286e19ea42f587b344ee6865t13d1516h2_174735a34e8a_b2149a751699
优势简单,广泛支持可模糊匹配,抗干扰

关键点:不同的 HTTP 库有不同的 JA3 指纹

客户端JA3 指纹
Chrome 120579ccef312d18482fc42e2b822ca2430
Firefox 1213b5074b1b5d032e5620f69f9f700ff0e
Python requests084c44f52a434da89e0b1bc98f8dd159
curl 默认51c64c77e60f3980eea90869b68c58a8

问题:如果你用 Python requests 访问服务器,即使设置了 User-Agent,服务器也能通过 JA3 识别出你不是真实浏览器


第 2 步:检测你的 TLS 指纹(10 分钟)

2.1 在线检测

方法 1:访问 JA3 检测网站

1
2
3
4
5
6
7
8
9
# 用 curl 测试
curl https://ja3er.com/json

# 用 Python requests 测试
python3 << 'EOF'
import requests
r = requests.get('https://ja3er.com/json')
print(r.text)
EOF
1
curl -s https://tls.peet.ws/api/all | jq .
  1. 打开 Wireshark
  2. 过滤器输入:tls.handshake.type == 1(只显示 Client Hello)
  3. 在终端执行请求:
1
curl https://example.com

接着展开 Transport Layer Security → Handshake Protocol: Client Hello

查看关键字段

1
2
3
4
5
6
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
- ...
Extension: supported_groups (len=10)
- secp256r1 (0x0017)
- x25519 (0x001d)
- ...

成功标志:你已经看到了自己客户端的 TLS 握手特征


第 3 步:获取目标指纹(5 分钟)

目标:获取真实浏览器或官方 App 的 JA3 指纹用于伪装

3.1 浏览器指纹

方法 1:直接查询

Chrome 浏览器访问 https://ja3er.com/json

记录显示的 JA3 哈希值。

方法 2:从 GitHub 数据库查询

访问 https://github.com/salesforce/ja3/blob/master/lists/osx-nix-ja3.csv

搜索 “Chrome”、“Safari”、“Firefox” 找到对应版本的 JA3。

3.2 android App 指纹

使用 Wireshark 抓取真实 App 的流量

  1. 配置手机走电脑代理
  2. Wireshark 监听对应网卡
  3. 打开目标 App,触发网络请求
  4. 过滤 tls.handshake.type == 1 找到 Client Hello 包
  5. 记录或导出该包

提取 JA3

1
2
3
4
5
6
7
8
# Use ja3 Tool(NeedInstall)
pip3 install pyshark
python3 << 'EOF'
import pyshark
cap = pyshark.FileCapture('capture.pcap', display_filter='tls.handshake.type == 1')
for pkt in cap:
print(pkt.tls.handshake_ciphersuite)
EOF

第 4 步:伪造 TLS 指纹(15 分钟)

4.1 使用 curl-impersonate(推荐)

curl-impersonate 是一个修改版的 curl,能完美模拟浏览器的 TLS 指纹。

安装(macOS):

1
2
3
4
5
# Use Homebrew
brew install curl-impersonate

# or downloadpre-compiled version
# https://github.com/lwthiker/curl-impersonate/releases
1
2
3
4
5
6
7
8
# 伪装成 Chrome 120
curl_chrome120 https://ja3er.com/json

# 伪装成 Firefox 121
curl_ff121 https://ja3er.com/json

# 伪装成 Safari 17
curl_safari17 https://ja3er.com/json

4.2 使用 Python tls-client 库

安装

1
pip3 install tls-client

基本使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import tls_client

# 创建会话,伪装成 Chrome 120
session = tls_client.Session(
    client_identifier="chrome_120",
    random_tls_extension_order=True
)

# 发送请求
response = session.get("https://ja3er.com/json")
print(response.json())

支持的客户端标识符

1
2
3
4
5
6
7
# 桌面浏览器
"chrome_103", "chrome_110", "chrome_120"
"firefox_102", "firefox_104", "firefox_121"
"safari_15_3", "safari_16_0", "safari_17_0"

# 移动端
"okhttp4_android_7", "okhttp4_android_8", "okhttp4_android_13"

自定义 JA3 字符串

1
2
3
4
5
6
import tls_client

session = tls_client.Session(
    client_identifier="custom",
    ja3_string="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-21,29-23-24,0"
)

4.3 使用 Go ja3transport 库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
    "fmt"
    "io"
    "net/http"
    "github.com/CUCyber/ja3transport"
)

func main() {
    // 创建带 JA3 指纹的 Transport
    tr, _ := ja3transport.NewTransport("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-21,29-23-24,0")

    client := &http.Client{Transport: tr}

    resp, _ := client.Get("https://ja3er.com/json")
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}

第 5 步:验证伪装效果(5 分钟)

创建对比脚本 compare_ja3.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash

echo "=== 原生 curl ==="
curl -s https://ja3er.com/json | jq -r '.ja3'

echo ""
echo "=== curl-impersonate (Chrome) ==="
curl_chrome120 -s https://ja3er.com/json | jq -r '.ja3'

echo ""
echo "=== Python requests ==="
python3 -c "import requests; print(requests.get('https://ja3er.com/json').json()['ja3'])"

echo ""
echo "=== Python tls-client ==="
python3 << 'EOF'
import tls_client
session = tls_client.Session(client_identifier="chrome_120")
print(session.get("https://ja3er.com/json").json()['ja3'])
EOF

运行对比

1
2
chmod +x compare_ja3.sh
./compare_ja3.sh

预期输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
=== 原生 curl ===
51c64c77e60f3980eea90869b68c58a8

=== curl-impersonate (Chrome) ===
579ccef312d18482fc42e2b822ca2430

=== Python requests ===
084c44f52a434da89e0b1bc98f8dd159

=== Python tls-client ===
579ccef312d18482fc42e2b822ca2430

实战示例 - 访问受保护的 API

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import tls_client

# 使用伪装的 TLS 指纹
session = tls_client.Session(client_identifier="chrome_120")

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}

response = session.get('https://api.example.com/protected', headers=headers)
print(response.status_code)
print(response.text)

伪装效果对比

工具User-AgentTLS 指纹服务器响应
requestsChromePython❌ 403 Forbidden
tls-clientChromeChrome✅ 200 OK

工作原理

TLS 握手过程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
客户端                              服务器
   |                                  |
   |--- Client Hello ---------------→|
   |    (JA3 指纹在此生成)             |
   |                                  |
   |←-- Server Hello ----------------|
   |    (Server JA3S 指纹)            |
   |                                  |
   |--- Key Exchange --------------→|
   |←-- Finished -------------------|
   |                                  |
   |←→ 加密数据传输 ←→|

JA3 指纹生成细节

原始字符串示例

1
2
3
4
5
6
7
771,4865-4866-4867-49195-49199,0-23-65281-10-11,29-23-24,0
 │        │                      │          │      │
 │        │                      │          │      └─ EC Point Formats
 │        │                      │          └─ Supported Groups (椭圆曲线)
 │        │                      └─ Extensions
 │        └─ Cipher Suites
 └─ TLS 版本 (771 = TLS 1.2)

JA4 指纹格式

1
2
3
4
5
6
7
8
t13d1516h2_174735a34e8a_b2149a751699
│  │  │  │  │            │
│  │  │  │  │            └─ Signature Algorithms 哈希
│  │  │  │  └─ Extensions 哈希(截断)
│  │  │  └─ h2 = HTTP/2
│  │  └─ 16 个 Extensions
│  └─ 15 个 Cipher Suites
└─ TLS 1.3
  • 可读性强(无需查表)

常见问题

❌ 问题 1: curl-impersonate 安装失败

症状:Homebrew 找不到 curl-impersonate

解决

1
2
3
4
5
6
# macOS/Linux: 手动下载预编译版本
wget https://github.com/lwthiker/curl-impersonate/releases/download/v0.6.1/curl-impersonate-v0.6.1.x86_64-linux-gnu.tar.gz

tar -xzf curl-impersonate-*.tar.gz
cd curl-impersonate-*
sudo cp curl_* /usr/local/bin/

❌ 问题 2: tls-client 不支持某个浏览器版本

症状ValueError: Unknown client identifier: chrome_999

解决:查看支持的客户端列表

1
2
import tls_client
print(tls_client.settings.ClientIdentifiers)

❌ 问题 3: 伪装后仍被检测

可能原因

  1. HTTP/2 指纹不匹配

    • JA3 指纹是浏览器,但 HTTP 头顺序/值不对
    • 解决:使用完整的浏览器模拟(包括 HTTP/2 特征)
  2. 行为特征异常

    • 请求速度太快
    • 缺少 Referer/Cookie
    • 解决:添加延迟、模拟真实用户行为
  3. IP 信誉问题

    • IP 被标记为数据中心/代理
    • 解决:使用住宅代理或轮换 IP
  4. 设备指纹

    • 服务器检测 Canvas 指纹、WebGL 指纹等
    • 解决:使用真实浏览器自动化(Selenium + undetected-chromedriver)

❌ 问题 4: 如何在 Frida 中修改 TLS 指纹?

场景:你想修改 Android App 的 TLS 指纹

方法:Hook Java 层 SSLSocket

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Java.perform(function () {
  var SSLSocket = Java.use("javax.net.ssl.SSLSocket");

  SSLSocket.setEnabledCipherSuites.implementation = function (suites) {
    console.log("[*] Original Cipher Suites:", suites);

    // 修改为目标指纹加密套件
    var customSuites = [
      "TLS_AES_128_GCM_SHA256",
      "TLS_AES_256_GCM_SHA384",
      "TLS_CHACHA20_POLY1305_SHA256",
    ];

    console.log("[*] ModifyAfter:", customSuites);
    return this.setEnabledCipherSuites(customSuites);
  };
});

延伸阅读

相关配方

工具深入

在线资源

理论基础


快速参考

常用工具对比

工具语言难度特点
curl-impersonateBash最简单,完美模拟
tls-clientPython易用,支持多种浏览器
ja3transportGo高性能,需要 Go 环境
requests + urllib3Python复杂,需深度定制

快速检测脚本

detect_ja3.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/bin/bash

echo "正在检测 TLS 指纹..."
echo ""

URL="https://ja3er.com/json"

# 检测当前客户端
JA3=$(curl -s "$URL" | jq -r '.ja3')
echo "你的 JA3: $JA3"

# 查询已知指纹
echo ""
echo "常见客户端 JA3:"
echo "  Chrome 120:  579ccef312d18482fc42e2b822ca2430"
echo "  Firefox 121: 3b5074b1b5d032e5620f69f9f700ff0e"
echo "  Safari 17:   4e2d5f6c3e8f7a9b0c1d2e3f4a5b6c7d"
echo "  Python req:  084c44f52a434da89e0b1bc98f8dd159"
echo "  curl:        51c64c77e60f3980eea90869b68c58a8"

# 对比
if [ "$JA3" == "579ccef312d18482fc42e2b822ca2430" ]; then
    echo ""
    echo "✅ 匹配: Chrome 120"
elif [ "$JA3" == "084c44f52a434da89e0b1bc98f8dd159" ]; then
    echo ""
    echo "⚠️ 匹配: Python requests (容易被识别)"
else
    echo ""
    echo "❓ 未知指纹"
fi

TLS 指纹伪装模板

browser_session.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""
TLS 指纹伪装模板
"""
import tls_client


class BrowserSession:
    """模拟浏览器会话"""

    PROFILES = {
        'chrome': 'chrome_120',
        'firefox': 'firefox_121',
        'safari': 'safari_17_0',
        'android': 'okhttp4_android_13'
    }

    def __init__(self, browser='chrome'):
        """初始化会话

        Args:
            browser: 浏览器类型 ('chrome', 'firefox', 'safari', 'android')
        """
        identifier = self.PROFILES.get(browser, 'chrome_120')
        self.session = tls_client.Session(
            client_identifier=identifier,
            random_tls_extension_order=True
        )
        self._set_headers(browser)

    def _set_headers(self, browser):
        """设置对应 HTTP 头"""
        user_agents = {
            'chrome': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'firefox': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
            'safari': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15',
            'android': 'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.144 Mobile Safari/537.36'
        }

        self.session.headers.update({
            'User-Agent': user_agents.get(browser, user_agents['chrome']),
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
            'Accept-Language': 'en-US,en;q=0.5',
            'Accept-Encoding': 'gzip, deflate, br',
            'DNT': '1',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1'
        })

    def get(self, url, **kwargs):
        """发送 GET 请求"""
        return self.session.get(url, **kwargs)

    def post(self, url, **kwargs):
        """发送 POST 请求"""
        return self.session.post(url, **kwargs)

    def verify_fingerprint(self):
        """验证 TLS 指纹"""
        r = self.get('https://ja3er.com/json')
        return r.json()


# 使用示例
if __name__ == '__main__':
    # 创建 Chrome 会话
    browser = BrowserSession('chrome')

    # 验证指纹
    print("验证 TLS 指纹...")
    result = browser.verify_fingerprint()
    print(f"JA3: {result['ja3']}")
    print(f"User-Agent: {result['User-Agent']}")

    # 发送请求
    response = browser.get('https://api.example.com/data')
    print(f"\n状态码: {response.status_code}")

相关内容

如果这篇文章对你有帮助,请我喝杯咖啡吧~
+5 支付宝支付宝
+5 微信微信