Intl API để lộ cơ sở dữ liệu locale ICU trong trình duyệt. Tìm hiểu lệnh Intl nào làm lộ dấu vân tay, vì sao lộ phiên bản bản dựng và vì sao khó giả mạo.
Hỏi đa số mọi người phần nào của API Intl trong JavaScript được dùng để lấy dấu vân tay, họ sẽ trả lời là múi giờ. Đó đúng là tín hiệu nổi tiếng nhất, và nó đã được phân tích kỹ trong bài Lộ múi giờ và ngôn ngữ. Nhưng Intl không phải một tín hiệu đơn lẻ. Nó là ô cửa nhìn vào cơ sở dữ liệu locale mà trình duyệt của bạn được đóng gói kèm, và cơ sở dữ liệu đó lớn, có phiên bản, khác nhau giữa các engine, nền tảng và bản dựng. Những câu hỏi bạn có thể đặt ra cho nó mang nhiều thông tin hơn hẳn một chuỗi múi giờ.
Điểm chính
Intlđể lộ dữ liệu locale kiểu ICU được biên dịch vào trình duyệt hoặc hệ điều hành, nên câu trả lời phụ thuộc vào engine, hệ điều hành và phiên bản bản dựng, chứ không chỉ ngôn ngữ bạn chọn.- Ba giá trị locale có thể mâu thuẫn nhau:
navigator.languages(khai báo), headerAccept-Language(gửi đi thực tế) vàIntl.DateTimeFormat().resolvedOptions().locale(do môi trường chạy thương lượng ra). Bản thân sự mâu thuẫn là một tín hiệu. Intl.supportedValuesOf()trả về danh sách dài các lịch, quy tắc sắp xếp, tiền tệ, hệ chữ số, múi giờ và đơn vị. Cả danh sách mới là dấu vân tay, và nó còn cho biết niên đại bản dựng của bạn.- Kết quả định dạng của
Intl.NumberFormat,Intl.DateTimeFormatvàIntl.Collatorcó thể khác nhau giữa các phiên bản dữ liệu, còn trên những trình duyệt tôn trọng cài đặt vùng của hệ điều hành, giá trị mặc định có thể làm lộ khu vực kể cả khi ngôn ngữ giao diện là tiếng Anh. - Vì dữ liệu nằm trong tệp nhị phân nên rất khó giả mạo cho nhất quán. Tor Browser và
privacy.resistFingerprintingcủa Firefox có thể chuẩn hóa chúng vềen-US, đánh đổi bằng sự tiện dụng.
Múi giờ: chỉ một đoạn ngắn
Đúng, Intl.DateTimeFormat().resolvedOptions().timeZone là điểm rò rỉ Intl nổi tiếng nhất, và việc nó lệch với quốc gia của IP là dấu hiệu VPN kinh điển. Ở đây chúng tôi không nhắc lại; chủ đề này thuộc về bài Lộ múi giờ và ngôn ngữ. Phần dưới đây nói về phần còn lại của bề mặt Intl, ít được bàn tới hơn nhiều.
Ba giá trị locale, ba nguồn gốc
Trình duyệt có nhiều khái niệm về "ngôn ngữ của bạn", và chúng đến từ những nơi khác nhau:
navigator.languageslà danh sách có thứ tự mà trình duyệt khai báo cho script.Accept-Languagelà thứ trình duyệt thực sự gửi đi trong mỗi yêu cầu.resolvedOptions().locale, có ởIntl.DateTimeFormatvà các đối tượng cùng họ, là kết quả môi trường chạy thương lượng sau khi đối chiếu yêu cầu của bạn với dữ liệu locale mà nó thực sự có.
Trên trình duyệt nguyên bản, ba giá trị này khớp nhau theo cách dự đoán được. Nếu một công cụ sửa hai giá trị đầu mà quên giá trị thứ ba, trang web sẽ thấy một danh tính được khai báo mà chính môi trường chạy không xác nhận. Đây là cùng một logic với tính nhất quán của dấu vân tay và phát hiện giả mạo User-Agent: không giá trị đơn lẻ nào là bằng chứng, chính sự mâu thuẫn mới là bằng chứng.
Mục có entropy cao nhất: Intl.supportedValuesOf()
Intl.supportedValuesOf(key) nhận một trong các khóa calendar, collation, currency, numberingSystem, timeZone hoặc unit và trả về danh sách đã sắp xếp các giá trị mà môi trường chạy hỗ trợ. Các danh sách này dài và đi theo phiên bản dữ liệu locale được đóng gói trong bản dựng.
Điểm quan trọng là cả danh sách mới là dấu vân tay, không phải một mục riêng lẻ nào. Thêm một loại tiền tệ mới, một múi giờ được đổi tên hoặc mới được nhận diện, hay thêm một đơn vị đều làm danh sách thay đổi. Vì phiên bản dữ liệu locale cập nhật cùng các bản phát hành của trình duyệt và hệ điều hành, danh sách còn đóng vai trò dấu ngày bản dựng: nó xếp bạn vào một khoảng phát hành mà không cần đọc chuỗi User-Agent. Băm sáu danh sách sẽ cho một mã định danh gọn, ổn định chừng nào dữ liệu của trình duyệt còn ổn định, và được chia sẻ với mọi người dùng cùng dữ liệu đó.
Kết quả định dạng như một đầu dò
Cách khác là thôi liệt kê và bắt đầu định dạng. Đưa cho môi trường chạy một đầu vào cố định rồi so sánh kết quả:
Intl.NumberFormatquyết định dấu phân cách nhóm, dấu thập phân, vị trí ký hiệu tiền tệ và cách viết rút gọn.Intl.DateTimeFormat.formatToParts()trả ngày tháng thành các phần có nhãn, nên thứ tự các phần và chuỗi ký tự nằm giữa chúng đều lộ ra.Intl.Collatorquyết định cách sắp xếp chuỗi có dấu, chữ hoa chữ thường lẫn lộn và chuỗi CJK.
Các phiên bản dữ liệu khác nhau và dữ liệu nền tảng khác nhau có thể cho kết quả khác biệt quan sát được với cùng một đầu vào. Chúng tôi cố ý không liệt kê chuỗi chính xác cho từng phiên bản, vì chúng thay đổi và chúng tôi chưa kiểm chứng; điều quan trọng là loại khác biệt. Bên theo dõi không cần biết đáp án đúng là gì, chỉ cần một giá trị băm ổn định từ nhiều đáp án.
Giá trị mặc định gắn với vùng của hệ điều hành, không phải ngôn ngữ
Tùy trình duyệt và nền tảng, một số giá trị mặc định của Intl đi theo cài đặt vùng của hệ điều hành, chứ không phải ngôn ngữ bạn chọn trong trình duyệt. Safari trên thiết bị Apple là ví dụ rõ nhất; Chrome và Firefox phần lớn lấy giá trị mặc định từ cài đặt ngôn ngữ của chính trình duyệt, và bản thân điều đó cũng là một khác biệt giữa các engine. Các giá trị có thể bị ảnh hưởng gồm:
- lịch mặc định (Gregorian, Phật lịch, niên hiệu Nhật Bản, v.v.);
- hệ chữ số mặc định (chữ số Latin, Ả Rập-Ấn Độ, Devanagari);
- chu kỳ giờ (12 giờ hay 24 giờ) và ngày đầu tuần.
Một người có thể đọc trang tiếng Anh trên trình duyệt có giao diện tiếng Anh trong khi hệ điều hành đặt vùng Thái Lan hoặc Ả Rập Xê Út. Ở trình duyệt tôn trọng cài đặt này, navigator.language báo tiếng Anh, còn resolvedOptions() lặng lẽ để lộ khu vực. Đó là tín hiệu tách biệt với header ngôn ngữ, và hữu ích để ước đoán thiết bị được thiết lập ở đâu.
Vì sao khó giả mạo cho nhất quán
Trình duyệt antidetect có thể viết lại User-Agent, navigator.languages và chuỗi múi giờ, vì đó là các giá trị trên đối tượng JavaScript. Còn bản thân dữ liệu locale được tích hợp sẵn trong tệp nhị phân hoặc hệ điều hành. Để giữ được sự nhất quán, lớp giả mạo phải chặn mọi hàm khởi tạo Intl, mọi khóa của supportedValuesOf, mọi lệnh gọi formatToParts, cả toLocaleString trên số, ngày và chuỗi, rồi trả lời khớp với nền tảng và phiên bản đã khai báo. Sót một chỗ là danh tính khai báo mâu thuẫn với môi trường chạy. Xem trình duyệt antidetect so với trình duyệt thật để biết những kẽ hở này được phát hiện ra sao trong thực tế.
Chúng tôi mô tả các tín hiệu để bạn hiểu và kiểm tra chúng, không phải như công thức để giả mạo.
Biện pháp giảm thiểu và cái giá
Tor Browser, và Firefox khi bật privacy.resistFingerprinting, xử lý bề mặt này bằng cách chuẩn hóa: múi giờ luôn được báo là UTC, và nếu bạn đồng ý yêu cầu phiên bản tiếng Anh của trang web, locale cũng được báo thống nhất là en-US cho mọi trang. Mọi người trong đám đông trông giống hệt nhau, đó chính là mục đích.
Cái giá là có thật: ngày, số và thứ tự sắp xếp được định dạng theo một locale không phải của bạn, và trang web tin vào giá trị được báo sẽ hiển thị sai quy ước cho người dùng ở Đức hay Nhật. Chuẩn hóa là đổi sự tiện lợi lấy ẩn danh, vì vậy đa số trình duyệt không bật mặc định.
Tín hiệu này lớn đến đâu?
Đánh giá thẳng thắn, Intl là nguồn entropy cỡ trung bình, không đạt tầm canvas. Một locale phổ biến như en-US trên Chrome bản hiện hành có cùng hồ sơ Intl với rất nhiều người. Tín hiệu có giá trị phân biệt cao hơn với locale hiếm, vùng hệ điều hành và trình duyệt lệch nhau, bản dựng cũ hoặc tùy biến, và giá trị lớn nhất là để đối chiếu chéo với các tín hiệu khác. Cách nhìn nhận chuyện này có trong bài về entropy và tập ẩn danh. Bạn có thể xem trình duyệt của mình báo gì bằng kiểm tra dấu vân tay, công cụ chạy hoàn toàn trong trình duyệt và không gửi gì lên máy chủ.

