Tìm hiểu cách HTTP/2 fingerprinting hoạt động: khung SETTINGS, thứ tự pseudo-header, và cách dấu vân tay Akamai bổ sung cho JA3/JA4 khi phát hiện bot.
Mỗi kết nối HTTPS đều bắt đầu bằng bắt tay TCP và đàm phán TLS, nhưng HTTP/2 fingerprinting không hoạt động ở hai giai đoạn đó. Nó trích xuất chữ ký từ các khung (frame) mà client gửi sau khi TLS được thiết lập — trong những mili-giây đầu tiên của phiên HTTP/2, trước khi bất kỳ yêu cầu nào được thực hiện. Tổ hợp chính xác của các khung cài đặt, mức tăng cửa sổ kiểm soát luồng và thứ tự pseudo-header mà client gửi được xác định bởi thư viện HTTP/2 của nó, không phải người dùng, và nhất quán giữa các yêu cầu. Sự nhất quán đó là thứ biến nó thành dấu vân tay.
Điểm mấu chốt
- HTTP/2 client tự tiết lộ trước khi gửi yêu cầu đầu tiên. Khung SETTINGS ban đầu, mức tăng WINDOW_UPDATE và các khung PRIORITY được quyết định bởi thư viện HTTP/2 — không phải người dùng — tạo ra các chữ ký khác nhau giữa Chrome, Firefox, Safari và các công cụ tự động hóa.
- Dấu vân tay Akamai mã hóa bốn tín hiệu — giá trị SETTINGS, mức tăng cửa sổ, các khung PRIORITY và thứ tự pseudo-header — thành một chuỗi gọn mà nhận diện HTTP client bên dưới.
- Nó bắt được bot qua TLS và kiểm tra User-Agent. Một script giả mạo TLS handshake của Chrome qua
curl-impersonatethường vẫn thất bại ở tầng HTTP/2 trừ khi nó cũng mô phỏng chính xác chuỗi khung và thứ tự pseudo-header của Chrome. - Kết hợp với JA3/JA4, hai dấu vân tay bao phủ các tầng bổ sung cho nhau: TLS nhận diện thư viện mã hóa; HTTP/2 fingerprinting nhận diện triển khai HTTP. Sự không khớp giữa chúng là tín hiệu bot mạnh.
- Các khung PRIORITY — phần đặc trưng nhất trong lịch sử của dấu vân tay HTTP/2 — đã bị loại bỏ trong RFC 9113, vì vậy fingerprinting hiện đại dựa nhiều hơn vào giá trị SETTINGS và thứ tự pseudo-header.
HTTP/2 là gì và tại sao nó có dấu vân tay?
HTTP/2 (RFC 7540, được cập nhật bởi RFC 9113) thay thế mô hình HTTP/1.1 văn bản thuần, xử lý một yêu cầu một lần bằng một lớp đóng khung nhị phân. Toàn bộ giao tiếp diễn ra trên một kết nối TCP duy nhất qua các luồng được đánh số, được mang bởi các khung nhị phân — mỗi khung có kiểu: DATA, HEADERS, SETTINGS, WINDOW_UPDATE, PRIORITY và nhiều kiểu khác.
Khi client mở kết nối HTTP/2, nó theo một chuỗi khởi động quy định trước khi có thể gửi bất kỳ yêu cầu nào:
- Gửi lời mở đầu kết nối HTTP/2 — một chuỗi ma thuật 24 byte cố định.
- Gửi khung SETTINGS khai báo các lựa chọn tham số của client cho kết nối này.
- Tùy chọn gửi khung WINDOW_UPDATE để tăng cửa sổ kiểm soát luồng ban đầu.
- Tùy chọn gửi các khung PRIORITY để xây dựng cây phụ thuộc luồng.
- Gửi khung HEADERS đầu tiên chứa yêu cầu HTTP thực tế.
Các bước 2–4 xảy ra trước khi máy chủ phản hồi và trước khi bất kỳ mã người dùng nào chạy. Chúng hoàn toàn được quyết định bởi thư viện HTTP/2 — và chúng khác nhau giữa các triển khai theo cách ổn định qua các phiên, URL và địa chỉ IP.
Bốn tín hiệu mà dấu vân tay Akamai nắm bắt
Các nhà nghiên cứu tại Akamai Technologies quan sát thấy các tín hiệu khởi động này đủ nhất quán cho mỗi client để được dùng làm dấu vân tay. Dấu vân tay Akamai mã hóa bốn tín hiệu thành một chuỗi phân tách bằng ký tự |. Một dấu vân tay điển hình trông như sau:
1:65536;3:1000;4:6291456;6:262144|15663105|0|m,a,s,p
1. Giá trị khung SETTINGS
Khung SETTINGS là danh sách các cặp id_tham_số:giá_trị. HTTP/2 định nghĩa sáu tham số chuẩn: HEADER_TABLE_SIZE (ID 1), ENABLE_PUSH (2), MAX_CONCURRENT_STREAMS (3), INITIAL_WINDOW_SIZE (4), MAX_FRAME_SIZE (5) và MAX_HEADER_LIST_SIZE (6). Client chỉ gửi các tham số mà nó muốn ghi đè giá trị mặc định của giao thức.
Tổ hợp của tham số nào xuất hiện, giá trị của chúng và thứ tự của chúng được quyết định bởi thư viện HTTP/2. Stack HTTP của Chrome dựa trên BoringSSL chọn giá trị và thứ tự khác với Necko của Firefox, CFNetwork của Safari hay httpx của Python. Ngay cả các cập nhật phiên bản nhỏ cũng có thể thay đổi tham số nào được đưa vào hoặc giá trị chúng mang. Phần đầu tiên của chuỗi dấu vân tay Akamai — 1:65536;3:1000;4:6291456;6:262144 trong ví dụ trên — mã hóa các cặp này.
2. Mức tăng WINDOW_UPDATE
Hệ thống kiểm soát luồng của HTTP/2 bắt đầu với cửa sổ mức kết nối mặc định là 65.535 byte. Hầu hết các client ngay lập tức gửi khung WINDOW_UPDATE để tăng nó lên kích thước làm việc lớn hơn, và mức tăng cụ thể mà chúng chọn là đặc trưng của client đó. Thiếu WINDOW_UPDATE hoặc giá trị mức tăng bất thường tự nó là một tín hiệu — nhiều thư viện HTTP tối giản bỏ qua hoàn toàn khung này, trong khi các trình duyệt chính gửi mức tăng lớn, đặc thù cho thư viện của họ. Phần thứ hai của dấu vân tay mã hóa giá trị này.
3. Chuỗi khung PRIORITY
HTTP/2 ban đầu cho phép client khai báo ưu tiên luồng bằng các khung PRIORITY, xây dựng cây phụ thuộc để máy chủ biết gửi phản hồi nào trước. Các trình duyệt gửi các mẫu khởi động đặc trưng: mẫu lịch sử của Chrome, chẳng hạn, bao gồm một chuỗi khung PRIORITY cụ thể cho các luồng 3, 5, 7, 1 và 11 với trọng số quy định. Những mẫu này cực kỳ ổn định và ngay lập tức phân biệt được với các HTTP client không phải trình duyệt thường bỏ qua hoàn toàn khung PRIORITY.
RFC 9113 (2022) loại bỏ cơ chế ưu tiên luồng HTTP/2 vì thấy nó phức tạp khi triển khai và hiếm khi cải thiện hiệu suất trong thực tế. Các client hiện đại ngày càng bỏ qua các khung PRIORITY. Phần thứ ba của dấu vân tay Akamai ghi lại mẫu này (hoặc sự vắng mặt của nó), vẫn có thể phân biệt các bản dựng trình duyệt cũ hơn với các HTTP client headless hoặc được script hóa.
4. Thứ tự pseudo-header
Các yêu cầu HTTP/2 mã hóa phương thức, đường dẫn, lược đồ và máy chủ dưới dạng pseudo-header — :method, :path, :scheme và :authority — phải đứng trước các header thông thường trong khung HEADERS. Giao thức yêu cầu cả bốn pseudo-header này xuất hiện trước các header thông thường, nhưng thứ tự giữa chúng với nhau là do triển khai quyết định.
Các trình duyệt nhất quán sử dụng một thứ tự cụ thể. Chrome thường gửi :method, :authority, :scheme, :path — được mã hóa trong dấu vân tay là m,a,s,p. Các thư viện HTTP trong Go, Python và Node.js thường phát các pseudo-header này theo thứ tự khác, hoặc thứ tự có thể thay đổi theo phiên bản. Phần thứ tư ghi lại thứ tự này và kết hợp với tín hiệu SETTINGS, nó là một trong những yếu tố phân biệt ổn định nhất giữa client trình duyệt thực và thư viện tự động hóa.
HTTP/2 fingerprinting bổ sung cho TLS fingerprinting như thế nào
TLS fingerprinting (JA3/JA4) trích xuất chữ ký từ thông điệp ClientHello — được gửi dạng văn bản thuần trước khi phiên TLS được mã hóa. HTTP/2 fingerprinting trích xuất chữ ký từ các khung nhị phân được gửi ngay sau khi TLS được thiết lập. Hai kỹ thuật hoạt động ở các tầng liền kề, không chồng chéo:
| Tầng | Kỹ thuật | Những gì được nắm bắt |
|---|---|---|
| TLS | JA3 / JA4 | Bộ mã hóa, tiện ích mở rộng, đường cong elliptic trong ClientHello |
| HTTP/2 | Dấu vân tay Akamai | Giá trị SETTINGS, mức tăng cửa sổ, chuỗi PRIORITY, thứ tự pseudo-header |
Bot đã clone thành công TLS handshake của Chrome bằng công cụ như curl-impersonate vẫn gửi khung HTTP/2 từ thư viện HTTP bên dưới của nó — bộ SETTINGS khác và thứ tự pseudo-header khác mà bất kỳ máy chủ có nhận thức về dấu vân tay nào cũng có thể phân biệt với trình duyệt thực. Kết hợp cả hai dấu vân tay tạo ra bài kiểm tra khó vượt qua hơn nhiều so với từng cái riêng lẻ.
Đây là cùng nguyên tắc tầng kép được mô tả trong hướng dẫn các kỹ thuật phát hiện bot: sự không nhất quán giữa các tầng giao thức quan sát được là bằng chứng mạnh hơn về giả mạo so với bất kỳ bất thường đơn lẻ nào.
Thứ tự HTTP header: một tín hiệu liên quan
Ngoài các tín hiệu đóng khung nhị phân, thứ tự của các HTTP header thông thường trong khung HEADERS cũng mang thông tin. Chrome, Firefox và Safari mỗi cái phát các header — Accept, Accept-Language, Accept-Encoding, User-Agent, Upgrade-Insecure-Requests — theo thứ tự nhất quán được xác định bởi logic chuẩn bị yêu cầu của trình duyệt, không phải bởi trang hay người dùng. Các thư viện tự động hóa phát header theo thứ tự mặc định khác — thường theo bảng chữ cái hoặc theo thứ tự chúng được thêm vào trong code.
Yêu cầu có thứ tự header khớp với requests của Python hay net/http của Go thay vì thứ tự chuẩn của Chrome — rất có thể không phải là trình duyệt thực, bất kể trường User-Agent tuyên bố gì. Fingerprinting theo thứ tự header hoạt động trên cả HTTP/1.1 và HTTP/2, và thường được sử dụng cùng với các tín hiệu đóng khung nhị phân để cho điểm tổng hợp phong phú hơn.
Kiểm tra thực tế
Không giống các tín hiệu browser fingerprinting như Canvas hay WebGL — có thể đọc được từ JavaScript ngay trong trang — HTTP/2 fingerprinting về bản chất là phía máy chủ. Các khung SETTINGS và WINDOW_UPDATE là đóng khung nhị phân không bao giờ chạm đến ngữ cảnh JavaScript của trang, vì vậy việc kiểm tra đòi hỏi một máy chủ đọc và phiên dịch các khung thô đó trước khi chuyển kết nối cho tầng ứng dụng.
Endpoint /http2 trên BrowserLeaks hiển thị dấu vân tay Akamai trực tiếp của bạn — chuỗi bốn phần mà client hiện tại của bạn đang gửi. Công cụ phát hiện bot của BrowserInsight bao phủ các tín hiệu phía client bổ sung (tạo phẩm tự động hóa, rò rỉ trình duyệt headless, tính nhất quán của dấu vân tay). Tầng TLS được hiển thị riêng qua trang chẩn đoán mạng.
Câu hỏi thường gặp
HTTP/3 có dấu vân tay tương tự không?
Có, mặc dù các tín hiệu khác nhau. HTTP/3 chạy qua QUIC thay vì TCP+TLS, và QUIC mang các tham số kết nối riêng — tham số truyền tải nhúng trong gói QUIC Initial — khác nhau theo triển khai. Các khung SETTINGS HTTP/3, được mang trong luồng điều khiển QPACK, cung cấp tín hiệu tương tự SETTINGS HTTP/2. Khi HTTP/3 trở nên phổ biến hơn, nghiên cứu tích cực đang mở rộng fingerprinting kiểu HTTP/2 sang HTTP/3.
Bot có thể vượt qua HTTP/2 fingerprinting bằng cách giả mạo User-Agent không?
Không. Header User-Agent được đặt trong khung HEADERS bởi code ứng dụng, nhưng các khung SETTINGS, WINDOW_UPDATE và PRIORITY được phát bởi thư viện HTTP/2 trước khi bất kỳ code ứng dụng nào chạy. Bot giả mạo User-Agent Chrome trong khi sử dụng net/http của Go hoặc httpx của Python vẫn gửi giá trị SETTINGS và thứ tự pseudo-header của thư viện đó, không phải của Chrome.
HTTP/2 fingerprinting có được dùng trong môi trường production ngày nay không?
Có. Các CDN lớn, nhà cung cấp quản lý bot và các trang web lớn sử dụng nó cùng với TLS fingerprinting và các tín hiệu hành vi như một phần của tính điểm bot đa tầng. Vì nó không yêu cầu thực thi JavaScript và nắm bắt các khung trước khi máy chủ gửi bất kỳ phản hồi nào, nó đặc biệt khó né tránh mà không có mô phỏng chính xác ở cấp thư viện.
Nếu PRIORITY hữu ích như vậy cho fingerprinting, tại sao lại loại bỏ nó?
PRIORITY đã bị xóa trong RFC 9113 vì những lý do hoàn toàn không liên quan đến fingerprinting — các nhà triển khai thấy nó phức tạp để hỗ trợ đúng cách và hiếm khi mang lại sự cải thiện độ trễ có thể đo được trong thực tế. Việc loại bỏ làm giảm một chiều của dấu vân tay cho các client hiện đại, nhưng điều đó cũng có nghĩa là bất kỳ client nào vẫn gửi mẫu PRIORITY cũ rất có thể đang chạy phiên bản trình duyệt cũ hơn — bản thân đó là một tín hiệu phân loại hữu ích.


