
Anh Tuan
Data Science Expert

Nếu bạn từng cố gắng quét một trang web được bảo vệ bởi hệ thống phát hiện bot cấp doanh nghiệp, bạn có thể đã gặp phải một bức tường vô hình: các yêu cầu của bạn bị chặn ngay cả khi header, cookie và User-Agent của bạn hoàn hảo. Lý do? Bắt chước TLS - và điều này xảy ra trước khi yêu cầu HTTP được gửi đi.
Các dịch vụ chống bot như Cloudflare, Akamai, DataDome và các dịch vụ khác kiểm tra cuộc trao đổi TLS thô để xác định xem client có phải là trình duyệt thực sự hay không hay là công cụ tự động hóa. Các client HTTP tiêu chuẩn - Go's net/http, Python's requests, curl, Node.js axios - tất cả đều có các dấu vân tay TLS riêng biệt bị phát hiện ngay lập tức.
Trong hướng dẫn này, bạn sẽ xây dựng một máy chủ Go nhẹ sử dụng httpcloak để giả mạo dấu vân tay TLS của Chrome thực sự, và kết nối nó với các quy trình n8n của bạn để mọi yêu cầu HTTP trông giống như lưu lượng truy cập trình duyệt Chrome thật sự ở cấp độ mạng.
Mỗi lần client kết nối với một trang web qua HTTPS, nó khởi tạo một cuộc trao đổi TLS bằng cách gửi một thông điệp ClientHello. Thông điệp này chứa:
Các dịch vụ chống bot trích xuất các giá trị này và tính toán một dấu vân tay - gọi là dấu vân tay JA3 hoặc JA4 - mà xác định duy nhất phần mềm client. Mỗi trình duyệt, thư viện HTTP và môi trường chạy ngôn ngữ lập trình đều tạo ra một dấu vân tay khác nhau.
| Client | Dấu vân tay JA3 | Được phát hiện là |
|---|---|---|
| Chrome 145 | Mã băm duy nhất khớp với trình duyệt Chrome | Trình duyệt thực sự |
| Firefox 130 | Mã băm khác - Firefox sử dụng các ưu tiên giao thức khác | Trình duyệt thực sự |
Go net/http |
Mã băm hoàn toàn khác - stack TLS của Go dễ nhận biết | Bot / công cụ tự động hóa |
Python requests |
Mã băm khác biệt - TLS của urllib3 của Python dễ nhận biết |
Bot / công cụ tự động hóa |
| curl | Mã băm khác - dấu vân tay TLS của curl đã được biết đến | Bot / công cụ tự động hóa |
Node.js axios |
Dấu vân tay TLS của Node.js - dễ bị phát hiện | Bot / công cụ tự động hóa |
Bài học chính: Bắt chước TLS xảy ra trong quá trình trao đổi, trước khi bất kỳ header HTTP nào được gửi đi. Không có cách nào để sửa chữa dấu vân tay TLS không phải trình duyệt bằng cách điều chỉnh header.
Khi trình duyệt kết nối với một trang web qua HTTPS, nó gửi một thông điệp ClientHello TLS bao gồm các chi tiết về các bộ giao thức, mở rộng và cài đặt được hỗ trợ. Các dịch vụ chống bot ghi lại dấu vân tay này (gọi là dấu vân tay JA3/JA4) và so sánh với các hồ sơ trình duyệt đã biết.
net/http của Go, requests của Python, curl và hầu hết các thư viện HTTP đều có dấu vân tay TLS riêng biệt. Ngay cả khi cookie và header đúng, các hệ thống chống bot sẽ chặn yêu cầu nếu chúng phát hiện dấu vân tay TLS không phải trình duyệt.
Dưới đây là các bước xảy ra:
ClientHello của nóĐây là lý do tại sao việc đặt User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... không giúp gì. User-Agent là một header cấp HTTP. Bắt chước TLS hoạt động ở cấp độ thấp hơn. Nếu User-Agent của bạn nói Chrome nhưng dấu vân tay TLS của bạn nói Go, yêu cầu sẽ bị phát hiện ngay lập tức.
Bắt chước TLS đã trở thành thực hành tiêu chuẩn trong bảo vệ bot doanh nghiệp. Dưới đây là các dịch vụ chính kiểm tra dấu vân tay TLS:
| Dịch vụ chống bot | Kiểm tra TLS | Ghi chú |
|---|---|---|
| Cloudflare Bot Management | Có | Thách thức "Verifying your browser..." toàn trang. Kiểm tra JA3/JA4 trên mỗi yêu cầu |
| Akamai Bot Manager | Có | Sử dụng bắt chước TLS như một trong nhiều tín hiệu trong điểm bot |
| DataDome | Có | Phân tích dấu vân tay TLS cùng với các tín hiệu hành vi |
| Nhiều dịch vụ khác | Thay đổi | Bắt chước TLS đang trở thành tiêu chuẩn trong bảo vệ bot doanh nghiệp |
CapSolver hỗ trợ giải quyết các thách thức từ nhiều dịch vụ này. Máy chủ TLS trong hướng dẫn này được thiết kế để hoạt động cùng với bất kỳ quy trình giải captcha nào mà yêu cầu HTTP cuối cùng cần trông giống như trình duyệt thực sự - dù bạn đang vượt qua Cloudflare Challenge, Akamai, DataDome hay bất kỳ hệ thống chống bot nào khác.
| Yêu cầu | Ghi chú |
|---|---|
| n8n tự host | Bắt buộc - máy chủ TLS phải chạy trên cùng máy với n8n. n8n Cloud không phù hợp. |
| Go 1.21+ | Phải được cài đặt trên máy chủ. Kiểm tra bằng go version. |
| Quản lý tiến trình (đề xuất) | Bất kỳ quản lý tiến trình nào (systemd, supervisor, Docker, PM2) để giữ cho máy chủ TLS chạy sau khi khởi động lại |
Máy chủ TLS là một máy chủ HTTP Go nhẹ, nhận yêu cầu trên cổng 7878 và chuyển tiếp chúng bằng cài đặt TLS Chrome-145 của httpcloak.
mkdir -p ~/tls-server && cd ~/tls-server
Tạo một tệp có tên main.go với nội dung sau:
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
"github.com/sardanioss/httpcloak/client"
)
type FetchRequest struct {
URL string `json:"url"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Proxy string `json:"proxy"`
Body string `json:"body"`
}
type FetchResponse struct {
Status int `json:"status"`
Body string `json:"body"`
Headers map[string][]string `json:"headers"`
}
type ErrorResponse struct {
Error string `json:"error"`
}
func writeError(w http.ResponseWriter, status int, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(ErrorResponse{Error: msg})
}
func fetchHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
writeError(w, http.StatusMethodNotAllowed, "only POST allowed")
return
}
var req FetchRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
return
}
if req.URL == "" {
writeError(w, http.StatusBadRequest, "url is required")
return
}
if req.Method == "" {
req.Method = "GET"
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
c := client.NewClient("chrome-145", client.WithTimeout(60*time.Second))
defer c.Close()
if req.Proxy != "" {
c.SetProxy(req.Proxy)
}
headers := make(map[string][]string, len(req.Headers))
var userAgent string
for k, v := range req.Headers {
lower := strings.ToLower(k)
if lower == "user-agent" {
userAgent = v
} else {
headers[k] = []string{v}
}
}
var bodyReader io.Reader
if req.Body != "" {
bodyReader = strings.NewReader(req.Body)
}
hcReq := &client.Request{
Method: strings.ToUpper(req.Method),
URL: req.URL,
Headers: headers,
Body: bodyReader,
UserAgent: userAgent,
FetchMode: client.FetchModeNavigate,
}
resp, err := c.Do(ctx, hcReq)
if err != nil {
writeError(w, http.StatusBadGateway, "fetch failed: "+err.Error())
return
}
body, err := resp.Text()
if err != nil {
writeError(w, http.StatusInternalServerError, "read body failed: "+err.Error())
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(FetchResponse{
Status: resp.StatusCode,
Body: body,
Headers: resp.Headers,
})
}
func main() {
const port = "7878"
mux := http.NewServeMux()
mux.HandleFunc("/fetch", fetchHandler)
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"status":"ok"}`)
})
log.Printf("TLS server (httpcloak chrome-145) listening on :%s", port)
log.Fatal(http.ListenAndServe(":"+port, mux))
}
go mod init tls-server
go get github.com/sardanioss/httpcloak/client
go build -o main main.go
./main
Máy chủ chạy ở chế độ nền. Để giữ cho nó chạy trong nền, sử dụng bất kỳ quản lý tiến trình nào (systemd, supervisor, Docker, v.v.) hoặc chạy nó trong phiên screen/tmux.
curl http://localhost:7878/health
Kết quả mong muốn: {"status":"ok"}
Lưu ý: Máy chủ TLS phải chạy trên máy chủ cùng với phiên bản n8n của bạn. Quy trình n8n gọi nó tại
http://localhost:7878/fetch.
Mặc định, n8n chặn các nút Yêu cầu HTTP từ gọi địa chỉ localhost (bảo vệ SSRF). Bạn cần tắt tính năng này để các quy trình của bạn có thể truy cập máy chủ TLS trên localhost:7878.
Thêm biến môi trường N8N_BLOCK_ACCESS_TO_LOCALHOST=false và khởi động lại n8n của bạn. Cách bạn làm điều này phụ thuộc vào cách bạn chạy n8n:
Nếu bạn chạy n8n trực tiếp:
export N8N_BLOCK_ACCESS_TO_LOCALHOST=false
n8n start
Nếu bạn sử dụng Docker:
Thêm -e N8N_BLOCK_ACCESS_TO_LOCALHOST=false vào lệnh docker run của bạn, hoặc thêm nó vào phần environment trong docker-compose.yml.
Máy chủ TLS cung cấp một điểm cuối duy nhất chấp nhận bất kỳ yêu cầu HTTP nào và chuyển tiếp nó với dấu vân tay TLS Chrome.
Điểm cuối: POST http://localhost:7878/fetch
Nội dung yêu cầu (JSON):
{
"url": "https://example.com",
"method": "GET",
"headers": {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
"cookie": "cf_clearance=abc123; session=xyz"
},
"proxy": "http://user:pass@host:port",
"body": ""
}
| Trường | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
url |
chuỗi | Có | URL đích để truy xuất |
method |
chuỗi | Không | Phương thức HTTP - mặc định là GET |
headers |
đối tượng | Không | Các cặp khóa-giá trị của header HTTP để gửi |
proxy |
chuỗi | Không | URL proxy theo định dạng http://user:pass@host:port |
body |
chuỗi | Không | Nội dung yêu cầu (đối với yêu cầu POST/PUT) |
Phản hồi (JSON):
{
"status": 200,
"body": "<html>...</html>",
"headers": { "content-type": ["text/html"], "..." : ["..."] }
}
Để gọi máy chủ TLS từ quy trình n8n, sử dụng nút Yêu cầu HTTP với các cài đặt sau:
| Tham số | Giá trị | Mô tả |
|---|---|---|
| Phương thức | POST |
Luôn POST đến máy chủ TLS |
| URL | http://localhost:7878/fetch |
Điểm cuối máy chủ TLS cục bộ |
| Loại nội dung | Raw |
Không sử dụng JSON - chế độ JSON của n8n serial hóa sai |
| Loại nội dung thô | application/json |
Thông báo cho máy chủ TLS rằng nội dung là JSON |
| Nội dung | ={{ JSON.stringify({ url: "...", method: "GET", headers: {...}, proxy: "..." }) }} |
Yêu cầu thực tế để chuyển tiếp |
Quan trọng: Sử dụng
contentType: "json"vớiJSON.stringify()trong nội dung khiến n8n serial hóa kép, gửi{"": ""}thay vì dữ liệu của bạn. Luôn sử dụngcontentType: "raw"vớirawContentType: "application/json".
Trong biểu thức nội dung nút Yêu cầu HTTP:
={{ JSON.stringify({
url: "https://protected-site.com/data",
method: "GET",
headers: {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"accept-language": "en-US,en;q=0.9"
},
proxy: "http://user:pass@proxy-host:8080"
}) }}
Máy chủ TLS sẽ chuyển tiếp yêu cầu này với dấu vân tay TLS Chrome-145, và trang đích sẽ nhận thấy kết nối trình duyệt Chrome thực sự.
Kiểm tra máy chủ TLS trực tiếp từ dòng lệnh:
curl -X POST http://localhost:7878/fetch \
-H "Content-Type: application/json" \
-d '{
"url": "https://tls-check.example.com",
"method": "GET",
"headers": {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
}
}'
Bạn có thể xác minh dấu vân tay TLS bằng cách hướng máy chủ đến trình kiểm tra JA3/JA4 - kết quả nên khớp với trình duyệt Chrome thực sự, không phải client thư viện Go.
"contentType": "thô",
"rawContentType": "application/json",
"body": "={{ JSON.stringify($json.body) }}",
"options": {
"timeout": 60000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
100,
0
],
"id": "tls00001-0001-0001-0001-000000000002",
"name": "Lấy dữ liệu qua máy chủ TLS"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
400,
0
],
"id": "tls00001-0001-0001-0001-000000000003",
"name": "Trả lời webhook"
}
],
"connections": {
"Nhận yêu cầu Solver": {
"main": [
[
{
"node": "Lấy dữ liệu qua máy chủ TLS",
"type": "main",
"index": 0
}
]
]
},
"Lấy dữ liệu qua máy chủ TLS": {
"main": [
[
{
"node": "Trả lời webhook",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
``
Bạn đã thiết lập một máy chủ giả mạo dấu vân tay TLS khiến các yêu cầu HTTP của n8n trông giống như lưu lượng truy cập trình duyệt Chrome thật sự ở cấp độ mạng. Điều này rất quan trọng để vượt qua các dịch vụ chống bot kiểm tra dấu vân tay TLS.
Máy chủ TLS này hữu ích để vượt qua:
Thư viện httpcloak với cài đặt Chrome-145 của nó xử lý giả mạo dấu vân tay JA3/JA4, khung SETTINGS HTTP/2, đàm phán ALPN và thứ tự tiêu đề — khiến các yêu cầu của bạn không thể phân biệt được với trình duyệt Chrome thực tế ở cấp độ TLS.
Cần giải CAPTCHA đồng thời với việc giả mạo TLS? Hãy kiểm tra CapSolver — nó tích hợp trực tiếp với n8n như một nút chính thức và hỗ trợ Cloudflare Challenge, Turnstile, reCAPTCHA và nhiều loại khác. Sử dụng mã khuyến mãi n8n để nhận thêm 8% khuyến mãi cho lần nạp tiền đầu tiên!

Dấu vân tay TLS là kỹ thuật mà các máy chủ phân tích các đặc điểm của tin nhắn ClientHello TLS của bạn — bao gồm các suite mã hóa, phần mở rộng và thứ tự của chúng — để xác định phần mềm nào đang thực hiện kết nối. Mỗi trình khách HTTP (Chrome, Firefox, curl, Go, Python) có mẫu dấu vân tay duy nhất.
Tiêu đề User-Agent là thuộc tính ở cấp độ HTTP. Phân tích dấu vân tay TLS xảy ra ở cấp độ thấp hơn — trong quá trình thiết lập TLS, trước khi bất kỳ tiêu đề HTTP nào được gửi. Các dịch vụ chống bot so sánh cả hai lớp: nếu User-Agent của bạn nói Chrome nhưng dấu vân tay TLS của bạn nói Go/Python, yêu cầu sẽ bị đánh dấu là bot.
httpcloak là thư viện Go giả mạo các hồ sơ TLS thực tế. Nó xử lý khớp dấu vân tay JA3/JA4, khung SETTINGS HTTP/2, đàm phán ALPN và thứ tự tiêu đề. Cài đặt chrome-145 khiến kết nối không thể phân biệt được với trình duyệt Chrome 145 thực tế.
Có. httpcloak hỗ trợ nhiều cài đặt trình duyệt. Xem tài liệu httpcloak để xem các cài đặt có sẵn. Để thay đổi cài đặt, chỉnh sửa client.NewClient("chrome-145", ...) trong main.go thành hồ sơ trình duyệt mong muốn của bạn.
Không dễ dàng. Máy chủ TLS là nhị phân Go cục bộ phải chạy trên cùng máy tính với n8n để các quy trình có thể gọi http://localhost:7878/fetch. n8n Cloud không cho phép chạy các dịch vụ cục bộ cùng với các quy trình. Bạn cần phiên bản n8n tự lưu trữ.
Có, nhưng bạn sẽ cần cập nhật URL trong các nút Yêu cầu HTTP của n8n từ http://localhost:7878/fetch thành http://địa chỉ_ip_máy_chủ:7878/fetch, và đảm bảo cổng 7878 có thể truy cập được. Bạn cũng sẽ cần vô hiệu hóa bảo vệ SSRF của n8n hoặc cho phép địa chỉ IP của máy chủ.
Cập nhật phụ thuộc httpcloak: go get -u github.com/sardanioss/httpcloak/client, thay đổi chuỗi cài đặt trong main.go thành phiên bản mới, xây dựng lại bằng go build -o main main.go, và khởi động lại máy chủ.
Có. Máy chủ HTTP của Go xử lý các yêu cầu đồng thời natively. Mỗi yêu cầu tạo ra một phiên bản client httpcloak mới với kết nối TLS riêng. Đối với khối lượng công việc cao, hãy theo dõi sử dụng bộ nhớ vì mỗi kết nối duy trì trạng thái TLS riêng.
Máy chủ TLS thêm độ trễ tối thiểu — thường là 10-50ms cho bước trung gian cục bộ. Phần lớn thời gian yêu cầu được dành cho kết nối HTTPS thực tế đến mục tiêu. Thủ tục thiết lập TLS của Chrome nặng hơn một chút so với mặc định của Go, nhưng điều này không đáng kể trong thực tế.
Sử dụng bất kỳ trình quản lý quy trình nào — systemd, supervisor, Docker, hoặc tương tự — để đăng ký máy chủ TLS như một dịch vụ khởi động cùng hệ thống. Đối với cài đặt nhanh, bạn cũng có thể chạy nó bên trong phiên làm việc screen hoặc tmux.
Xây dựng API giải eCAPTCHA v2/v3 bằng CapSolver và n8n. Tìm hiểu cách tự động hóa việc giải token, gửi token đến website và trích xuất dữ liệu được bảo vệ mà không cần lập trình.

Hãy tìm hiểu cách tích hợp CapSolver với n8n để giải quyết bài toán GeeTest V3 và xây dựng các quy trình tự động hóa đáng tin cậy.
