Đổi user-agent hiếm khi qua mắt được ai. Tìm hiểu cách website phát hiện giả mạo qua thứ tự header, client hints, dò engine JS và mâu thuẫn vân tay.
Giả mạo user-agent là hành động thay đổi chuỗi user-agent mà trình duyệt của bạn gửi đi để một website nghĩ rằng bạn đang dùng một trình duyệt, thiết bị hay hệ điều hành khác. Việc này rất dễ làm — chỉ một thiết lập hay một tiện ích mở rộng là đủ — nhưng nó hiếm khi qua mặt được một website thực sự để ý, vì user-agent chỉ là một trong hàng chục tín hiệu mà trình duyệt của bạn phát ra, còn những tín hiệu khác thì âm thầm mâu thuẫn với lời nói dối đó. Bài hướng dẫn này giải thích cách việc giả mạo bị phát hiện: tính nhất quán của header, client hints, dò engine JavaScript, và sự mâu thuẫn vân tay.
Tóm tắt nhanh
- Chuỗi user-agent chỉ là một lời tuyên bố tự khai báo và có thể chỉnh sửa tùy ý — riêng nó thì máy chủ không kiểm chứng được gì, nên việc phát hiện hiện đại coi nó là một khẳng định chưa được kiểm chứng cần được đối chiếu.
- Trình duyệt còn để lộ hàng chục tín hiệu khác (thứ tự header HTTP, các header
sec-*, User-Agent Client Hints, hành vi engine JavaScript, canvas/WebGL/màn hình/cảm ứng), và tất cả phải khớp với danh tính được tuyên bố. - Giả mạo thất bại vì làm giả một dòng văn bản thì dễ, còn làm giả toàn bộ hành vi quan sát được của một trình duyệt một cách nhất quán thì rất khó.
- Việc phát hiện mang tính xác suất: mỗi mâu thuẫn làm tăng điểm nghi ngờ chứ không đưa ra phán quyết có/không dứt khoát.
- Hành vi ở cấp engine và tính nhất quán của header HTTP nằm trong số những tín hiệu khó giả mạo nhất, vì chúng đòi hỏi tái tạo toàn bộ phần triển khai của một trình duyệt thật.
Giả mạo user-agent là gì?
Mỗi yêu cầu mà trình duyệt của bạn tạo ra đều bao gồm một chuỗi user-agent — một dòng văn bản như Mozilla/5.0 ... Chrome/120.0 ... công bố trình duyệt, phiên bản và nền tảng. Giả mạo nghĩa là cố ý thay đổi chuỗi đó. Người ta làm vậy vì nhiều lý do: quyền riêng tư (hòa lẫn vào đám đông hoặc phá vỡ việc theo dõi), truy cập nội dung bị giới hạn cho một số trình duyệt nhất định, cào dữ liệu web, kiểm thử tự động, hoặc — ở phía độc hại — gian lận và lưu lượng bot giả dạng người dùng thật.
Bản thân việc thay đổi chuỗi là chuyện vặt. Vấn đề là user-agent chỉ là một lời tuyên bố, và một trình duyệt còn để lộ hàng chục sự thật khác về chính mình mà máy chủ có thể đối chiếu với lời tuyên bố đó. Khi chúng không khớp nhau, sự giả mạo bị phơi bày.
Vì sao không thể tin riêng chuỗi user-agent
Một máy chủ chỉ đọc user-agent là tin lời trình duyệt nói về chính nó. Nhưng user-agent là thứ tự khai báo và có thể chỉnh sửa tùy ý, nên tự nó chẳng chứng minh được gì. Việc phát hiện hiện đại coi nó là một khẳng định chưa được kiểm chứng rồi đặt câu hỏi: liệu mọi thứ khác mà trình duyệt này làm có thực sự khớp với một bản trình duyệt thật mà nó tuyên bố hay không? Thường là không, vì giả mạo chuỗi thì dễ trong khi giả mạo toàn bộ hành vi quan sát được của một trình duyệt thì rất khó.
Cách website phát hiện một user-agent bị giả mạo
Việc phát hiện hoạt động bằng cách thu thập các tín hiệu độc lập và kiểm tra tính nhất quán nội tại giữa chúng. Các kỹ thuật chính:
Tính nhất quán của header HTTP (thứ tự và các header sec-*)
Các trình duyệt khác nhau gửi những header HTTP khác nhau, theo một thứ tự khác nhau, với những giá trị khác nhau. Chrome gửi một tập các header sec-ch-ua và sec-fetch-* mà Firefox không có; vị trí của header User-Agent trong yêu cầu khác nhau giữa các trình duyệt; và các giá trị Accept, Accept-Language, Accept-Encoding tuân theo những khuôn mẫu đặc thù của từng trình duyệt. Nếu một yêu cầu tuyên bố là Chrome nhưng lại thiếu các header sec-* của Chrome — hoặc sắp xếp header giống Firefox — thì lời tuyên bố đó đáng ngờ. Không có gì trong số này hiển thị trong chính chuỗi user-agent, và hầu hết các công cụ giả mạo không buồn tái tạo chúng.
Mâu thuẫn giữa Client Hints và user-agent
Các trình duyệt Chromium phơi bày User-Agent Client Hints, một nguồn thông tin trình duyệt/nền tảng có cấu trúc, đọc được bằng JavaScript thông qua API navigator.userAgentData. Một máy chủ có thể yêu cầu các client hints có entropy cao và so sánh chúng với chuỗi user-agent. Nếu user-agent nói "Safari trên macOS" nhưng API client hints lại tồn tại và báo cáo Chromium trên Windows, thì hai thứ này mâu thuẫn nhau và sự giả mạo trở nên rõ ràng — vì một Safari thật sẽ chẳng phơi bày client hints của Chromium chút nào.
Dò engine JavaScript và các tính năng
Mỗi engine triển khai JavaScript và nền tảng web hơi khác nhau, và những khác biệt đó có thể quan sát được trong mã. Một bộ phát hiện có thể kiểm tra xem những API lẽ ra phải tồn tại đối với trình duyệt được tuyên bố có hiện diện hay không, dò các đặc điểm đặc thù của engine, hoặc chạy các micro-benchmark mà thời gian thực thi của chúng lấy dấu vân tay của engine JavaScript. Một "Firefox" phơi bày các API chỉ có ở Chrome, hoặc có hành vi engine khớp với V8 thay vì SpiderMonkey, là bị giả mạo. Điều này gắn trực tiếp với việc trình duyệt thực sự dùng engine dựng hình nào — engine khó giả mạo hơn nhiều so với cái tên.
Mâu thuẫn vân tay (canvas, WebGL, màn hình, cảm ứng)
Cuối cùng, bản thân thiết bị để lộ những tín hiệu lẽ ra phải khớp với trình duyệt và nền tảng được tuyên bố:
- Canvas và WebGL dựng hình khác nhau tùy GPU, driver và hệ điều hành; một user-agent di động được hậu thuẫn bởi chữ ký GPU của máy tính để bàn là một dấu hiệu cảnh báo. Xem phát hiện vân tay canvas để biết cách thức hoạt động.
- Kích thước màn hình lớn hơn cửa sổ hiển thị, hoặc không khớp với loại thiết bị được tuyên bố, là điều đáng ngờ.
- Hỗ trợ cảm ứng: một user-agent của điện thoại trên một thiết bị báo cáo không có sự kiện cảm ứng hiếm khi hợp lý.
Mỗi tín hiệu riêng lẻ đều yếu, nhưng cùng nhau chúng tạo thành một hồ sơ mà user-agent phải khớp — và một thay đổi chuỗi đơn giản không thể theo kịp.
Cách tín hiệu phía máy chủ và phía máy khách mâu thuẫn với một UA bị giả mạo
Lý do việc giả mạo sụp đổ là nó phải lừa cùng lúc hai người quan sát rất khác nhau: máy chủ đọc HTTP thô, và JavaScript chạy bên trong trang. Một công cụ giả mạo vá được một bên thường quên mất bên kia, và hai câu chuyện thôi không còn ăn khớp.
Ở phía máy chủ, trước khi bất kỳ script nào chạy, bản thân yêu cầu đã mang theo dấu vết. Tập hợp và thứ tự các header HTTP/2, sự hiện diện của các header sec-ch-ua chỉ có ở Chromium, và TLS ClientHello đều được quan sát trước khi một byte nào của trang được phân tích. Đặc biệt, quá trình bắt tay TLS tạo ra một chữ ký ổn định — thường được tóm gọn thành một hash JA3 hoặc JA4 — bắt nguồn từ các bộ mã hóa và phần mở rộng mà máy khách đưa ra. Chữ ký đó phản ánh ngăn xếp mạng bên dưới (BoringSSL trong Chromium, NSS trong Firefox), chứ không phải chuỗi user-agent, nên một yêu cầu tuyên bố là Firefox nhưng lại trình ra vân tay TLS của Chromium thì tự nó đã mâu thuẫn. Không thể thay đổi bất kỳ điều nào trong số này bằng cách chỉnh user-agent trong thiết lập trình duyệt.
Ở phía máy khách, JavaScript có thể tra hỏi môi trường chạy một cách trực tiếp. navigator.userAgentData hoặc tồn tại (Chromium) hoặc không (Firefox, Safari) — và một chuỗi bị giả mạo không thể biến API này thành hiện hữu. Ngoài ra, sự có mặt hay vắng mặt của các global và API đặc thù của engine, cách chính xác mà số và ngày được tuần tự hóa, cách diễn đạt thông báo lỗi, và đặc tính thời gian của các vòng lặp chặt chẽ đều để lộ engine JavaScript nào thực sự đang chạy. Ngay cả CSS cũng tố cáo engine: các thuộc tính có tiền tố nhà cung cấp, cách dựng mặc định của các điều khiển biểu mẫu, và các cờ hỗ trợ được truy vấn qua CSS.supports() khác nhau giữa Blink, Gecko và WebKit. Bộ phát hiện chỉ đơn giản hỏi liệu những sự thật phía máy khách này có ăn khớp với điều mà các header và user-agent tuyên bố hay không.
Tuyên bố vs thực tế: một vài ví dụ điển hình
| Danh tính được tuyên bố | Một bản thật sẽ cho thấy | Một giả mạo vụng về thường cho thấy |
|---|---|---|
| Safari trên macOS | Không có header sec-ch-ua; navigator.userAgentData undefined; dấu hiệu CSS của WebKit | Header sec-ch-ua của Chromium và userAgentData đã được định nghĩa |
| Firefox trên Windows | Thứ tự header của Gecko; thời gian của SpiderMonkey; không có Chromium client hints | Hành vi engine kiểu V8 và chữ ký TLS/JA3 của Chromium |
| iPhone Safari (di động) | Có sự kiện cảm ứng; GPU di động trong WebGL; màn hình nhỏ | Chuỗi GPU renderer của máy tính để bàn và không hỗ trợ cảm ứng |
| Chrome trên Android | sec-ch-ua-mobile: ?1; chuỗi GPU ARM | sec-ch-ua-mobile: ?0 hoặc chuỗi GPU NVIDIA/Intel của máy tính để bàn |
Mỗi hàng là một mâu thuẫn mà bộ phát hiện có thể gắn cờ mà không cần "chứng minh" user-agent là giả — nó chỉ cần nhận ra rằng hai tín hiệu độc lập không thể cùng đúng.
Cách tự kiểm tra
Bạn không cần một trang trại máy chủ để thấy điều này. Mở console DevTools của trình duyệt và chạy navigator.userAgent cùng navigator.userAgentData cạnh nhau: trên Chromium thứ hai trả về một đối tượng, trên Firefox và Safari thì là undefined, bất kể bạn đã giả mạo chuỗi nào. Dùng bảng network để xem các header yêu cầu thực tế mà trình duyệt gửi đi và xác nhận sec-ch-ua có hiện diện hay không. Sau đó đổi user-agent bằng một tiện ích mở rộng hoặc mô phỏng thiết bị trong DevTools rồi lặp lại — bạn sẽ thấy chuỗi thay đổi trong khi client hints, hành vi engine và chuỗi GPU vẫn y nguyên như cũ. Khoảng chênh lệch đó chính là thứ mà các hệ thống phát hiện đo lường.
Kiểm tra trình duyệt của chính bạn xem có mâu thuẫn không
Bạn có thể tự thấy nhiều tín hiệu này trên chính mình. Chạy công cụ kiểm tra nhân trình duyệt của BrowserInsight để so sánh trình duyệt được tuyên bố của bạn với engine dựng hình thật của nó, và công cụ phát hiện bot để thấy các tín hiệu nhất quán mà một hệ thống tự động sẽ cân nhắc. Nếu bạn dùng các công cụ riêng tư làm thay đổi user-agent, đây cũng là cách bạn có thể phát hiện liệu chúng có tạo ra những mâu thuẫn mới khiến bạn dễ nhận diện hơn thay vì khó hơn hay không.
Câu hỏi thường gặp
Thay đổi user-agent có phạm pháp không?
Không. Thay đổi user-agent là một việc bình thường, hợp pháp mà các trình duyệt và tiện ích mở rộng cho phép bạn làm, và nó có những công dụng chính đáng như kiểm thử và tương thích. Vấn đề pháp lý chỉ phát sinh từ những gì bạn làm với nó — gian lận hay truy cập trái phép — chứ không phải từ hành động thay đổi chuỗi.
Một website có thể phát hiện giả mạo với độ chắc chắn 100% không?
Không phải lúc nào cũng chắc chắn, nhưng thường là với độ tin cậy cao. Việc phát hiện mang tính xác suất: mỗi sự mâu thuẫn (thiếu header, client hints không khớp, hành vi engine sai, vân tay xung đột) làm tăng một điểm nghi ngờ. Một sự giả mạo cẩn thận giữ mọi tín hiệu nhất quán thì khó bắt, nhưng những thay đổi user-agent thông thường để lại nhiều dấu vết.
Các công cụ riêng tư và tiện ích chống vân tay có gây cảnh báo giả không?
Đôi khi có. Các công cụ thay đổi user-agent hoặc ngẫu nhiên hóa tín hiệu có thể tạo ra những mâu thuẫn mà hệ thống phát hiện đọc là giả mạo, đó là một lý do người dùng coi trọng quyền riêng tư thỉnh thoảng gặp thêm các thử thách bot. Các công cụ được thiết kế tốt thay đổi tín hiệu một cách nhất quán để tránh điều này; những công cụ vụng về thì khiến bạn nổi bật hơn.
Tín hiệu nào đáng tin nhất để phát hiện giả mạo?
Không có một tín hiệu đơn lẻ nào — độ tin cậy đến từ việc kết hợp chúng. Dù vậy, hành vi ở cấp engine và tính nhất quán của header HTTP nằm trong số những thứ khó giả mạo nhất, vì chúng đòi hỏi phải tái tạo toàn bộ phần triển khai của một trình duyệt thật, chứ không chỉ chỉnh sửa một dòng văn bản.
Kết luận
Giả mạo user-agent thì dễ thử nhưng khó thành công, vì một trình duyệt tiết lộ về chính mình nhiều hơn rất nhiều so với chuỗi mà nó tự khai. Thứ tự header và các header sec-*, client hints, hành vi engine JavaScript, và các vân tay thiết bị đều phải khớp với lời tuyên bố — và một chỉnh sửa một dòng không thể giữ chúng đồng bộ. Dù bạn đang bảo vệ một website hay kiểm tra chính thiết lập riêng tư của mình, bài học vẫn như nhau: đừng bao giờ tin riêng user-agent, và hãy kiểm tra các tín hiệu xung quanh nó.
Đọc thêm: