Nhận diện người dùng trên web trong nhiều năm qua đã phụ thuộc phần lớn vào Client-side Storage như Cookie, localStorage hoặc indexedDB. Tuy nhiên, sự xuất hiện của các chính sách bảo mật nghiêm ngặt (ITP của Safari, Privacy Sandbox của Chrome), chế độ ẩn danh (Incognito Mode) và hành vi xóa cookie định kỳ của người dùng khiến các phương thức truyền thống trở nên kém ổn định.
Browser Fingerprinting (Dấu vân tay số trình duyệt) nổi lên như một kỹ thuật nhận diện thiết bị dựa hoàn toàn trên các đặc tính phần cứng, hệ điều hành và engine kết xuất của trình duyệt. Bài viết này sẽ bóc tách chi tiết từng thành phần cấu thành nên một fingerprint chất lượng cao, đồng thời phân tích lý do vì sao loại bỏ resolution (độ phân giải màn hình) lại là chìa khóa giúp mở rộng vô số trường hợp sử dụng thực tế.
Browser Fingerprinting là phương pháp thu thập một tập hợp các thuộc tính công khai mà trình duyệt cung cấp thông qua các Web API tiêu chuẩn. Bằng cách băm (hash) toàn bộ các tín hiệu này lại với nhau, hệ thống tạo ra một định danh duy nhất (Visitor ID) đại diện cho thiết bị đó.
Kỹ thuật này sở hữu những ưu điểm vượt trội:
Một hệ thống fingerprint mạnh mẽ không dựa vào một thông số đơn lẻ mà kết hợp nhiều tầng entropy (độ biến thiên) từ phần cứng đến phần mềm.
Hầu hết các thiết bị hiện đại đều sử dụng GPU để kết xuất đồ họa web. Thông qua WebGL:
WEBGL_debug_renderer_info, ứng dụng có thể đọc trực tiếp tên chipset và driver card màn hình (ví dụ: Apple M2 Max, NVIDIA GeForce RTX 4070 Laptop GPU, AMD Radeon RX 7800 XT).Tương tự WebGL, Canvas 2D vẽ một đoạn văn bản mẫu chứa ký tự đặc biệt, biểu tượng cảm xúc (emoji) và các đường cong Bezier.
AudioContext fingerprinting không thu âm micro hay phát ra tiếng ồn bên ngoài. Kỹ thuật này hoạt động hoàn toàn trong bộ nhớ bằng OfflineAudioContext:
sampleHash duy nhất cho thiết bị.Chuẩn IEEE-754 về tính toán dấu phẩy động (floating-point) cho phép tồn tại sai số cực nhỏ ở các chữ số thập phân cuối cùng khi tính toán các hàm lượng giác phức tạp.
Math.sin, Math.cos, Math.tan, Math.asin, Math.acos và đặc biệt là các giá trị lớn (Math.largeTan, Math.largeCos), các kiến trúc vi xử lý khác nhau (Intel Core x86_64, AMD Zen, Apple Silicon ARM64, Qualcomm Snapdragon) sẽ cho ra kết quả sai khác ở những chữ số thập phân thứ 16 trở đi.Mỗi hệ thống có một bộ sưu tập font chữ riêng biệt được cài đặt qua thời gian (font đồ họa, font văn phòng, font bản địa).
monospace, sans-serif), hệ thống có thể xác định chính xác font nào đang thực sự tồn tại trên máy người dùng.Các thông số cấu hình vật lý cơ bản:
navigator.deviceMemory).navigator.hardwareConcurrency).navigator.languages), ví dụ ["vi-VN", "vi", "en-US"].Asia/Ho_Chi_Minh) và độ lệch UTC offset.Resolution lại mở ra vô số Use-Case thực tế?Theo cách tiếp cận truyền thống, hầu hết các thư viện fingerprint cơ bản đều gom screen.width, screen.height và screen.resolution vào chuỗi băm nhận diện. Tuy nhiên, trong môi trường sản xuất thực tế, độ phân giải là thuộc tính cực kỳ dễ vỡ (fragile entropy).
Việc loại bỏ screen.resolution ra khỏi entropy băm để tạo thành Resilient Fingerprint (Dấu vân tay bền bỉ) mang lại bước nhảy vọt về độ ổn định cho các bài toán sau:
Đây là kịch bản phổ biến nhất của dân văn phòng và lập trình viên:
resolution bị thay thế hoàn toàn.Trong thiết lập đa màn hình (Dual/Triple Monitor), người dùng thường xuyên kéo cửa sổ trình duyệt từ màn hình chính sang màn hình phụ có độ phân giải và tỉ lệ scale khác nhau. Resilient Fingerprint đảm bảo không có hiện tượng gián đoạn hay gãy phiên khi người dùng di chuyển cửa sổ.
Trên điện thoại thông minh và máy tính bảng (iPhone, iPad, Android):
Người dùng iPad (Stage Manager / Split View), Windows (Snap Assist) hay Linux/macOS (i3, Hyprland, Yabai) thường xuyên làm việc với các khung hình được chia đôi hoặc resize động. Tách biệt độ phân giải giúp hệ thống nhận diện hoàn toàn miễn nhiễm trước sự thay đổi kích thước này.
Người dùng thường đổi mức DPI scaling trên Windows (100% → 125% → 150%) hoặc bấm Ctrl + / Ctrl - để phóng to thu nhỏ giao diện. Bỏ resolution giúp nhận diện không bị phụ thuộc vào thị lực hay sở thích phóng to màn hình của từng cá nhân.
Để tạo ra một Resilient Fingerprint, bạn có thể sử dụng thư viện @feelai/thumbmark với cấu hình loại trừ screen.resolution:
```typescript import { Thumbmark } from "@feelai/thumbmark"; import { UAParser } from "ua-parser-js";
export const getResilientFingerprint = async () => { // Loại bỏ thuộc tính resolution dễ vỡ const client = new Thumbmark({ exclude: ["screen.resolution"], });
const info = await client.get(); const ua = UAParser(navigator.userAgent); info.platform = ua.os.name ?? "Unknown";
return { thumbmarkId: info.thumbmark, platform: info.platform, components: info.components, version: info.version, }; }; ```
Để tăng cường độ độc bản, server (Cloudflare Worker hoặc Next.js Edge Route) có thể kết hợp dữ liệu client gửi lên với các headers mạng không thể giả mạo:
```typescript export const createServerMasterFingerprint = async ( req: Request, clientData: any, ) => { const serverInfo = { requestIp: req.headers.get("CF-Connecting-IP") || "127.0.0.1", country: (req as any).cf?.country || req.headers.get("cf-ipcountry") || "", region: (req as any).cf?.region || req.headers.get("x-cf-region") || "", timezone: (req as any).cf?.timezone || req.headers.get("x-cf-timezone") || "", };
// Loại bỏ resolution khỏi object screen trước khi tính băm const { screen, ...otherComponents } = clientData.components || {}; const cleanScreen = screen ? { ...screen, resolution: undefined } : {};
const entropyComponents = [ serverInfo.requestIp, serverInfo.country, serverInfo.region, serverInfo.timezone, clientData.platform, clientData.thumbmarkId, JSON.stringify(otherComponents.audio), JSON.stringify(otherComponents.fonts), JSON.stringify(otherComponents.hardware), JSON.stringify(otherComponents.locales), JSON.stringify(otherComponents.math), JSON.stringify(cleanScreen), JSON.stringify(otherComponents.webgl), ];
const encoder = new TextEncoder(); const digestBuffer = await crypto.subtle.digest( "SHA-256", encoder.encode(entropyComponents.join("|")), );
const hashHex = Array.from(new Uint8Array(digestBuffer)) .map((b) => b.toString(16).padStart(2, "0")) .join("");
return { masterFingerprint: hashHex.slice(0, 32), serverInfo, }; }; ```
Bạn có thể quan sát trực tiếp toàn bộ các thông số WebGL, AudioContext, sai số CPU vi phân và danh sách font chữ của chính thiết bị mình đang sử dụng bằng công cụ FeelAI Fingerprint Inspector:
👉 Trải nghiệm ngay tại: FeelAI Fingerprint Inspector
Công cụ cho phép bạn chuyển đổi linh hoạt giữa Standard Hash (có tính độ phân giải) và Resilient Hash (loại bỏ độ phân giải) để trực tiếp kiểm chứng cơ chế giữ vững định danh khi resize hoặc thay đổi màn hình hiển thị.