Trình duyệt giải mã được định dạng ảnh, video nào sẽ lộ engine, phiên bản và cả GPU. So sánh Accept, canPlayType và decodingInfo.
Các trình duyệt đến nay vẫn chưa thống nhất về việc giải mã được những định dạng ảnh và video nào. JPEG XL là ví dụ rõ nhất: Safari giải mã được nó từ Safari 17, còn Chromium và Firefox thì nhiều lần thay đổi quyết định về việc hỗ trợ chính thức. Mỗi khi hai engine trả lời khác nhau về việc mình giải mã được gì, câu trả lời cho câu hỏi "bạn hỗ trợ những định dạng nào?" lại có thể dùng để nhận diện chính trình duyệt đó. Bài này bàn về bề mặt fingerprinting ấy: khả năng giải mã ảnh và media, có thể đọc theo ba cách, trong đó một cách chạm tới tận phần cứng của bạn.
Điểm chính
- Hỗ trợ định dạng được đọc ở ba tầng: header HTTP
Accept(không cần JavaScript), các truy vấn năng lực nhưcanPlayType()vàisTypeSupported(), vàMediaCapabilities.decodingInfo()— trả thêmsmoothvàpowerEfficient. - Tự thân nó khá thô. Hỗ trợ định dạng gắn chặt với họ trình duyệt và phiên bản đến mức phần lớn chỉ lặp lại User-Agent. Đừng kỳ vọng nó chỉ ra một cá nhân cụ thể.
- Nó có ích theo hai cách: làm phép kiểm tra mâu thuẫn với UA tự khai, và cung cấp các bit giải mã phần cứng mà UA không có.
powerEfficientlà trường thú vị nhất. Nó cho biết một codec có được giải mã bằng phần cứng hay không, phản ánh GPU, driver và hệ điều hành — không cần WebGL, không cần hộp thoại cấp quyền.- Nó thay đổi theo nhịp khác các tín hiệu khác. Hỗ trợ định dạng thay đổi theo bản cập nhật trình duyệt, hệ điều hành và driver, chứ không theo nhịp thay đổi của canvas hay phông chữ.
Cách đọc 1: header Accept
Lần đọc đầu tiên xảy ra trước khi bất kỳ script nào chạy. Yêu cầu tải trang hoặc ảnh mang theo header Accept liệt kê các định dạng ảnh mà trình duyệt chấp nhận. Chromium gần đây gửi kiểu như image/avif,image/webp,image/apng,image/svg+xml,image/*, còn các engine khác gửi danh sách ngắn hơn hoặc sắp xếp khác. Đó là điều máy chủ thấy ngay ở yêu cầu đầu tiên, và bài fingerprinting chỉ bằng HTTP nói về cách cả bộ header được đọc mà không cần JavaScript. Danh sách định dạng nhanh chóng lộ engine và phiên bản gần đúng, và không công cụ chặn script nào bạn cài chạm được tới nó.
Lý do nó thay đổi theo thời gian rất đơn giản: trình duyệt thêm một token khi phát hành một bộ giải mã. Tài liệu MDN về các loại tệp ảnh liệt kê các định dạng cùng kiểu MIME, còn các trang Can I Use cho AVIF và cho JPEG XL cho thấy mức hỗ trợ không đồng đều đến đâu. AVIF nay gần như phổ quát; JPEG XL vẫn chia đôi, và chính vì các trình duyệt chưa thống nhất nên nó trở thành một tín hiệu có khả năng phân biệt.
Cách đọc 2: truy vấn năng lực bằng JavaScript
Script có thể hỏi thẳng thay vì suy ra từ header. HTMLMediaElement.canPlayType() nhận một kiểu MIME cùng chuỗi codec tùy chọn và trả về "", "maybe" hoặc "probably". MediaSource.isTypeSupported() làm điều tương tự cho các pipeline streaming, còn ảnh có thể được dò bằng cách tải một mẫu data URI rất nhỏ của từng định dạng và xem nó có giải mã được không.
const probes = [
'video/mp4; codecs="avc1.42E01E"',
'video/mp4; codecs="hev1.1.6.L93.B0"',
'video/webm; codecs="vp9"',
'video/mp4; codecs="av01.0.05M.08"',
'audio/ogg; codecs="opus"',
];
const v = document.createElement('video');
const vector = probes.map((p) => v.canPlayType(p) || 'no').join('|');
Kết quả là một chuỗi ngắn có tổ hợp giá trị thay đổi theo engine, phiên bản và hệ điều hành, vì các codec độc quyền như HEVC thường phụ thuộc vào framework media của nền tảng chứ không chỉ trình duyệt. Lưu ý rằng khác biệt giữa "maybe" và "probably" tự nó là khác biệt giữa các bản triển khai, chứ không chỉ là "có" hay "không".
Cách đọc 3: MediaCapabilities và các bit phần cứng
Lần đọc thứ ba là lý do bài này đáng có riêng. navigator.mediaCapabilities.decodingInfo() nhận một cấu hình đầy đủ — codec, độ phân giải, bitrate, tốc độ khung hình — và trả về ba giá trị boolean: supported, smooth và powerEfficient.
const info = await navigator.mediaCapabilities.decodingInfo({
type: 'file',
video: {
contentType: 'video/mp4; codecs="hev1.1.6.L93.B0"',
width: 3840, height: 2160, bitrate: 20_000_000, framerate: 60,
},
});
// { supported: true, smooth: true, powerEfficient: true }
powerEfficient về thực chất cho biết có giải mã bằng phần cứng hay không. Máy có GPU giải mã HEVC hoặc AV1 4K bằng phần cứng sẽ trả lời khác máy phải lùi về giải mã phần mềm, và câu trả lời phụ thuộc vào thế hệ GPU, driver và media stack của hệ điều hành. Dò một lưới các codec, kích thước và tốc độ khung hình sẽ cho ra một vector nhỏ mang đặc trưng phần cứng — thông tin mà User-Agent không có. Nó là phiên bản phía API media của các tín hiệu GPU trong bài fingerprinting WebGPU, đồng thời là dữ liệu cho các phép kiểm tra chéo trong bài lệch giữa vân tay và phần cứng, mà không cần cấp quyền hay dùng canvas.
Thành thật mà nói, entropy được bao nhiêu
Tự thân thì không nhiều. Hai người chạy cùng phiên bản Chrome trên cùng hệ điều hành báo gần như cùng danh sách Accept và vector canPlayType, nên nửa phần mềm của bề mặt này chủ yếu lặp lại họ trình duyệt và phiên bản. Nói thẳng: hỗ trợ định dạng không phải là định danh độc lập mạnh.
Nó trở nên hữu ích theo hai cách:
- Kiểm tra mâu thuẫn. Một "Safari" không giải mã được HEVC, hay một "Chrome mới" thiếu AV1, là điều không khớp. Các hệ thống phát hiện dùng nó cùng với phát hiện trình duyệt headless, nơi thiếu codec là dấu hiệu kinh điển, và trong các kiểm tra nhất quán rộng hơn.
- Các bit phần cứng.
powerEfficientvàsmoothtrên một lưới dò tách được các máy cùng bản trình duyệt nhưng khác GPU, điều mà UA không làm được.
Cần phân biệt với một chủ đề lân cận: fingerprinting WebRTC đọc các codec mà trình duyệt khai báo cho cuộc gọi thời gian thực, còn bài này nói về khả năng giải mã media lưu trữ, media streaming và ảnh.
Độ ổn định: thay đổi theo nhịp riêng
Tín hiệu canvas và phông chữ thay đổi khi bạn cài phông hoặc cập nhật ngăn xếp đồ họa. Hỗ trợ định dạng thay đổi khi trình duyệt phát hành hoặc bỏ một bộ giải mã, khi hệ điều hành cập nhật framework media, hoặc khi bản cập nhật driver bật hay tắt giải mã phần cứng. Điều đó cho tín hiệu này dạng bậc thang gắn với chu kỳ cập nhật. Với người muốn giữ định danh ổn định, đây là con dao hai lưỡi: đáng tin giữa các lần cập nhật, rồi đột ngột khác đi. Với người phòng chống theo dõi, bản cập nhật đúng là làm thay đổi phần hồ sơ này, nhưng vì rất nhiều người cập nhật gần như cùng lúc, bạn chủ yếu chuyển sang một đám đông mới chứ không trở nên nổi bật.
Bạn có thể làm gì
Không có công tắc nào cho việc này. Hỗ trợ định dạng là tính năng chức năng, và nói dối về nó sẽ làm hỏng video và ảnh. Các lựa chọn thực tế:
- Dùng trình duyệt phổ biến, luôn cập nhật để câu trả lời của bạn khớp với đám đông lớn thay vì một tổ hợp hiếm gặp.
- Khi có thể, ưu tiên các chế độ chống fingerprinting giới hạn những gì API năng lực media tiết lộ; chúng hy sinh một phần tính năng để thu nhỏ bề mặt.
- Cảnh giác với công cụ giả mạo UA mà không khớp hồ sơ giải mã — sự không khớp đó chính là thứ tín hiệu này bắt được.
Để xem trình duyệt của bạn thực sự báo gì và có khớp với các tín hiệu khác không, hãy chạy kiểm tra vân tay.
Câu hỏi thường gặp
Hỗ trợ codec có phải là vân tay mạnh không? Không. Nó thô, bám sát phiên bản trình duyệt và chủ yếu lặp lại User-Agent. Giá trị nằm ở kiểm tra mâu thuẫn và các bit giải mã phần cứng.
Chặn JavaScript có ngăn được nó không?
Chỉ ngăn các cách đọc bằng JavaScript. Header Accept vẫn được gửi ở yêu cầu đầu tiên.
Vì sao powerEfficient tiết lộ nhiều hơn supported?
supported theo bản dựng trình duyệt, còn powerEfficient theo GPU, driver và hệ điều hành của bạn.


