navigator.permissions.query() để lộ trạng thái granted/prompt/denied trên khoảng 15 quyền — dấu vân tay hỗ trợ và trạng thái không cần thao tác nào.
Hầu hết các kỹ thuật lấy dấu vân tay đều cần canvas, GPU, hoặc bộ đệm âm thanh. Permissions API không cần bất kỳ thứ nào trong số đó — nó được tạo ra để trang web có thể lịch sự kiểm tra "tôi có quyền định vị không?" trước khi hỏi xin, nhưng chỉ cần truy vấn qua hơn chục tên quyền mà các trình duyệt thực sự hỗ trợ, câu trả lời tự nó đã biến thành một dấu vân tay nhỏ, có cấu trúc — không cần cửa sổ hỏi, không cần cú nhấp chuột, và không đụng đến một pixel nào.
Tóm tắt nhanh
navigator.permissions.query({name})trả về mộtPermissionStatus—granted,denied, hoặcprompt— cho một quyền cụ thể, mà không bao giờ hiển thị hộp thoại cho người dùng.- Chromium hỗ trợ khoảng 15 đến 17 tên quyền có thể truy vấn; Firefox và Safari hỗ trợ ít hơn nhiều, nên tên nào thực sự phân giải được thay vì báo lỗi tự nó đã là một tín hiệu về trình duyệt/engine.
- Trạng thái
granted/denied/promptcủa mỗi quyền phản ánh sự kết hợp giữa lịch sử của trang web đó và các thiết lập ở cấp hệ điều hành, nên vector tổng hợp là một đóng góp bán bền vững vào dấu vân tay, gắn theo từng nguồn gốc (origin). - Thứ tự liệt kê tên quyền, và việc tên nào báo lỗi
TypeErrorvì không được hỗ trợ, khác nhau theo engine và phiên bản — một lớp dấu vân tay về hỗ trợ và thứ tự chồng lên chính các trạng thái đó. - Công cụ kiểm tra dấu vân tay của BrowserInsight hiển thị tín hiệu này cùng với các tín hiệu khác mà trình duyệt của bạn đang để lộ.
navigator.permissions.query() thực sự làm gì
Permissions API, được chuẩn hóa trong đặc tả W3C Permissions, cho phép script kiểm tra trạng thái quyền mà không kích hoạt lời nhắc xin quyền mà một tính năng như định vị hoặc thông báo thường sẽ hiển thị. Gọi như sau:
const status = await navigator.permissions.query({ name: 'geolocation' });
console.log(status.state); // "granted" | "denied" | "prompt"
Không có hộp thoại nào xuất hiện. Trình duyệt chỉ đơn giản báo cáo những gì nó đã biết: nguồn gốc này có được cấp quyền định vị trước đó hay không, đã bị từ chối rõ ràng hay chưa, hoặc nếu tính năng thực sự được yêu cầu thì có cần hỏi người dùng hay không. Đó chính là toàn bộ mục đích của API này — để trang web chỉ hiển thị "bật định vị" khi thực sự cần hỏi, thay vì đoán trước.
Truy vấn qua từng tên quyền
Phần thú vị, xét từ góc độ lấy dấu vân tay, là API này chấp nhận rất nhiều tên quyền khác nhau, và một script chỉ cần lặp qua tất cả:
// Phác thảo minh họa — không phải tên nào dưới đây cũng được hỗ trợ trên mọi trình duyệt
const candidateNames = [
'geolocation', 'notifications', 'push', 'midi', 'camera', 'microphone',
'background-sync', 'persistent-storage', 'ambient-light-sensor',
'accelerometer', 'gyroscope', 'magnetometer', 'screen-wake-lock',
'clipboard-read', 'clipboard-write', 'display-capture', 'nfc',
];
async function fingerprintPermissions() {
const results = {};
for (const name of candidateNames) {
try {
const status = await navigator.permissions.query({ name });
results[name] = status.state; // granted | denied | prompt
} catch {
results[name] = 'unsupported'; // tên này báo lỗi — trình duyệt không nhận ra nó
}
}
return results; // được đưa vào hàm băm cùng các tín hiệu khác
}
Vòng lặp này cho ra hai trục độc lập với nhau. Trục thứ nhất là tên nào phân giải được, tên nào báo lỗi — đây là dấu vân tay về hỗ trợ, gần với việc phát hiện tính năng hơn là trạng thái, và nó tương ứng khá trực tiếp với engine và phiên bản trình duyệt. Trục thứ hai là trạng thái thực tế trả về cho mỗi tên được hỗ trợ — phần này được định hình bởi lịch sử của từng người dùng cụ thể trên nguồn gốc đó và bởi thiết lập quyền ở cấp hệ điều hành, nên nó khác nhau giữa người này với người khác, kể cả trên cùng một trình duyệt giống hệt nhau.
Vì sao kết quả khác nhau giữa các trình duyệt
Các trình duyệt dựa trên Chromium (Chrome, Edge, Opera, Brave) nhận diện tập tên rộng nhất — khoảng 15 đến 17 tên tùy phiên bản, bao gồm một số quyền liên quan đến cảm biến và phần cứng như accelerometer, magnetometer, và ambient-light-sensor mà các engine khác hoàn toàn không để lộ qua API này. Firefox hỗ trợ một tập nhỏ hơn nhiều — geolocation, notifications, persistent-storage, và push là những tên đáng tin cậy — còn truy vấn các tên chỉ dành riêng cho Chromium sẽ báo lỗi thay vì phân giải được. Cách triển khai WebKit của Safari còn hẹp hơn nữa, và từ trước đến nay luôn chậm hơn cả về tốc độ áp dụng lẫn số lượng tên được nhận diện.
Khoảng cách này không phải do lười biếng; mỗi quyền liên quan đến cảm biến đó tương ứng với một device API mà engine thực sự đã triển khai, nên việc navigator.permissions.query({ name: 'magnetometer' }) báo lỗi trên Firefox là hệ quả trực tiếp, đáng tin cậy của việc Firefox đơn giản là không triển khai bề mặt Generic Sensor API mà quyền này bảo vệ. Kết quả thực tế cho việc lấy dấu vân tay là: tập tên được hỗ trợ thu hẹp phạm vi họ trình duyệt và phiên bản chỉ với rất ít code — tương tự cách khác biệt về hỗ trợ tính năng và API nói chung để lộ danh tính engine, chỉ khác là ở đây "tính năng" bị phát hiện chính là hệ thống quyền.
Đáng để phân biệt điều này với một mô hình quyền khác, hoàn toàn không liên quan: những gì một tiện ích mở rộng đã cài được phép làm (đọc mọi trang, truy cập cookie, chạy script) được quyết định bởi quyền manifest của chính tiện ích đó, chứ không phải bởi navigator.permissions.query() — xem Rủi ro quyền riêng tư của tiện ích mở rộng trình duyệt để biết cơ chế cấp quyền riêng biệt, rộng hơn nhiều đó hoạt động ra sao.
Vector trạng thái như một nguồn entropy
Bên cạnh việc tên nào phân giải được, trạng thái mà mỗi tên phân giải tới lại tạo thành một lớp tín hiệu thứ hai. Hầu hết người dùng chưa bao giờ chủ động cấp hoặc từ chối hầu hết các quyền, nên trường hợp phổ biến là gần như mọi quyền đều ở trạng thái prompt — một giá trị mặc định có entropy thấp. Nhưng bất kỳ quyền nào người dùng từng tương tác — cấp quyền định vị cho một trang, chặn thông báo trên toàn trình duyệt trong phần cài đặt, cấp lưu trữ bền vững cho một ứng dụng web — sẽ chuyển mục tương ứng thành granted hoặc denied, và giữ nguyên như vậy cho đến khi người dùng thay đổi lại.
Điều này khiến vector trở thành một tín hiệu lai: chủ yếu là dấu vân tay trình duyệt/engine hình thành từ mô hình hỗ trợ, cộng thêm một đóng góp nhỏ hơn nhưng có thật từ lịch sử quyền thực tế của từng người dùng. Kết hợp với các tín hiệu thụ động khác — canvas, WebGL, hoặc số lượng thiết bị media — nó bổ sung thêm một trục gần như độc lập mà script có thể đọc miễn phí, chỉ bằng một lệnh gọi bất đồng bộ, không kèm theo bất kỳ lời nhắc xin quyền nào của riêng nó.
Cách giảm thiểu
Không có công tắc "chặn permissions.query" chuyên dụng nào trong các trình duyệt phổ biến, bởi API này tồn tại chính là để các trang web hợp pháp tránh hỏi quá nhiều lần — loại bỏ nó sẽ khiến mọi tính năng bị giới hạn bởi quyền trở nên tệ hơn khi sử dụng. Các biện pháp giảm thiểu thực tế cũng giống như những gì hạn chế các dấu vân tay dựa trên kết xuất nói chung:
privacy.resistFingerprintingcủa Firefox chuẩn hóa một số hành vi liên quan đến quyền như một phần của chiến lược đồng nhất tổng thể, giảm bớt sự khác biệt của mô hình hỗ trợ so với đường cơ sở của Firefox.- Giảm thiểu những gì bạn cấp phép. Mỗi quyền bạn thực sự cấp hoặc từ chối rõ ràng đều trở thành một bit ổn định, có thể truy vấn được. Từ chối những quyền bạn không cần, và thu hồi các quyền cũ trong phần cài đặt trang web của trình duyệt, giữ cho phần lớn vector của bạn ở mức mặc định
promptcó entropy thấp. - Cố ý chọn trình duyệt có tập tên được hỗ trợ hẹp hơn. Đây là một sự đánh đổi thực sự, không phải chiến thắng miễn phí — phạm vi hỗ trợ hẹp hơn của Safari và Firefox cũng đồng nghĩa ít thứ để lấy dấu vân tay qua API cụ thể này hơn, đổi lại một số tính năng web sẽ kém đi.
- Kiểm tra xem bạn đang để lộ những gì. Chạy công cụ kiểm tra dấu vân tay của BrowserInsight để xem trạng thái quyền, canvas, WebGL, và các tín hiệu khác mà trình duyệt của bạn đang tiết lộ, tất cả cùng một chỗ.
Câu hỏi thường gặp
Truy vấn quyền có cần tương tác của người dùng hoặc lời nhắc không?
Không. navigator.permissions.query() chỉ báo cáo trạng thái đã có sẵn mà không hiển thị bất kỳ hộp thoại nào. Chỉ khi thực sự sử dụng một tính năng bị giới hạn bởi quyền (như gọi getCurrentPosition() để định vị) mới kích hoạt lời nhắc, và chỉ khi trạng thái là prompt chứ không phải đã là granted hoặc denied.
Kỹ thuật này có thể tự nó định danh được tôi không?
Không đáng tin cậy lắm khi đứng riêng. Hầu hết người dùng có hầu hết quyền ở trạng thái mặc định prompt, nên vector trạng thái tự nó có entropy hạn chế. Nó hữu ích nhất với bên theo dõi khi kết hợp với mô hình hỗ trợ của trình duyệt (tương quan với engine/phiên bản) và các tín hiệu thụ động khác như canvas hay phông chữ.
Vì sao một số tên quyền báo lỗi thay vì trả về trạng thái?
Bởi vì trình duyệt hoàn toàn không triển khai tính năng nền tảng đó. navigator.permissions.query({ name: 'magnetometer' }) báo lỗi trên trình duyệt không hỗ trợ Generic Sensor API, vì không tồn tại hệ thống quyền nào để kiểm tra trạng thái cho một tính năng vốn không tồn tại trong engine đó.
resistFingerprinting có ngăn được kỹ thuật này không?
privacy.resistFingerprinting của Firefox giảm bớt một phần khác biệt trong hành vi liên quan đến quyền như một phần chiến lược đồng nhất chung, nhưng API nền tảng và hành vi báo cáo trạng thái của nó vẫn còn đó — nó thu hẹp tín hiệu chứ không loại bỏ hoàn toàn, giống hệt cách nó xử lý canvas và các API bố cục.
Kết luận
Permissions API là một tiện ích nhỏ, hợp lý — kiểm tra trước khi hỏi — nhưng lại vô tình để lộ miễn phí hai tín hiệu độc lập: trình duyệt thực sự nhận diện những tên quyền nào, và mỗi tên hiện đang ở trạng thái gì. Không cái nào ồn ào khi đứng riêng, nhưng khi chồng lên hàng chục tín hiệu thụ động khác mà một trang có thể đọc mà không cần hỏi, đây lại là một lý do nữa cho thấy "lấy dấu vân tay" có ý nghĩa vượt xa canvas và WebGL.
Đọc thêm:


