Canvas, Âm thanh và WebGL: một phân tích sâu về các công nghệ vân tay
28/10/25


Markus_automation
Expert in data parsing and automation
Các trang web từ lâu đã học cách nhận diện khách truy cập không chỉ qua cookies. Ngay cả khi bạn xóa lịch sử duyệt web, bật chế độ riêng tư và thay đổi IP, trình duyệt của bạn vẫn có thể rò rỉ vân tay kỹ thuật số của bạn. Định danh ẩn này được tạo ra từ nhiều đặc điểm kỹ thuật của hệ thống. Nó theo dõi bạn trong mỗi lần truy cập.
Một vân tay trình duyệt là sự kết hợp của dữ liệu về thiết bị và môi trường phần mềm của bạn. Cũng như không có hai bông tuyết nào giống hệt nhau, rất hiếm khi có hai trình duyệt hoàn toàn giống nhau. Các thông số đơn lẻ (chẳng hạn như phiên bản trình duyệt hoặc độ phân giải màn hình) xuất hiện trên hàng nghìn người dùng. Nhưng sự kết hợp của hàng chục chi tiết như vậy tạo ra một hồ sơ riêng biệt. Tệ hơn là, vân tay không được lưu trữ trên thiết bị của bạn: nó được tính toán "ngay lập tức" bởi trang web. Vì lý do đó, vân tay không biến mất khi bạn xóa cookies hoặc sử dụng chế độ riêng tư: mỗi lần kịch bản sẽ thu thập lại các thuộc tính giống nhau.
Bên cạnh các thành phần vân tay đơn giản như tác nhân người dùng, múi giờ hoặc ngôn ngữ trình duyệt, có ba kỹ thuật nâng cao để thu thập nó: Canvas, Ngữ cảnh âm thanh và WebGL. Cả ba đều tận dụng khả năng đồ họa và âm thanh tích hợp của trình duyệt để trích xuất thông tin về phần cứng và phần mềm của bạn. Nhưng chính xác thì chúng hoạt động như thế nào? Tại sao chúng lại tiết lộ những khác biệt gần như không thể nhận ra giữa các thiết bị? Hãy phân tích nó.
Các trang web từ lâu đã học cách nhận diện khách truy cập không chỉ qua cookies. Ngay cả khi bạn xóa lịch sử duyệt web, bật chế độ riêng tư và thay đổi IP, trình duyệt của bạn vẫn có thể rò rỉ vân tay kỹ thuật số của bạn. Định danh ẩn này được tạo ra từ nhiều đặc điểm kỹ thuật của hệ thống. Nó theo dõi bạn trong mỗi lần truy cập.
Một vân tay trình duyệt là sự kết hợp của dữ liệu về thiết bị và môi trường phần mềm của bạn. Cũng như không có hai bông tuyết nào giống hệt nhau, rất hiếm khi có hai trình duyệt hoàn toàn giống nhau. Các thông số đơn lẻ (chẳng hạn như phiên bản trình duyệt hoặc độ phân giải màn hình) xuất hiện trên hàng nghìn người dùng. Nhưng sự kết hợp của hàng chục chi tiết như vậy tạo ra một hồ sơ riêng biệt. Tệ hơn là, vân tay không được lưu trữ trên thiết bị của bạn: nó được tính toán "ngay lập tức" bởi trang web. Vì lý do đó, vân tay không biến mất khi bạn xóa cookies hoặc sử dụng chế độ riêng tư: mỗi lần kịch bản sẽ thu thập lại các thuộc tính giống nhau.
Bên cạnh các thành phần vân tay đơn giản như tác nhân người dùng, múi giờ hoặc ngôn ngữ trình duyệt, có ba kỹ thuật nâng cao để thu thập nó: Canvas, Ngữ cảnh âm thanh và WebGL. Cả ba đều tận dụng khả năng đồ họa và âm thanh tích hợp của trình duyệt để trích xuất thông tin về phần cứng và phần mềm của bạn. Nhưng chính xác thì chúng hoạt động như thế nào? Tại sao chúng lại tiết lộ những khác biệt gần như không thể nhận ra giữa các thiết bị? Hãy phân tích nó.
Nội dung
Vân tay Canvas
Canvas API trong trình duyệt cho phép vẽ đồ họa thông qua JavaScript. Khả năng này được sử dụng một cách gián tiếp để lấy vân tay. Một đoạn mã trên trang tạo ra một phần tử <canvas> vô hình và thực hiện một loạt các lệnh vẽ (chẳng hạn như, hiển thị văn bản, hình học, thêm bóng), sau đó đọc hình ảnh kết quả dưới dạng mảng pixel. Mỗi thiết bị tạo ra các biến thể nhỏ trong đầu ra pixel cuối cùng do sự khác biệt trong phần cứng và phần mềm của nó. Một mã hash sau đó được tính toán từ dữ liệu đó và được sử dụng làm một nhận dạng duy nhất cho người dùng.
Quy trình có thể được miêu tả từng bước như sau:
Tạo Canvas. Trang web được ghé thăm chèn động một phần tử
<canvas>vào trang (thường bị ẩn khỏi màn hình).Vẽ. Mã JS hiển thị đồ họa kiểm tra vào canvas đó. Thông thường, nó vẽ một chuỗi văn bản bằng một phông chữ hiếm, thêm những hình dạng màu, đường nét và hiệu ứng (chuyển màu, bóng).
Đọc các pixel. Sau khi vẽ, kịch bản gọi
toDataURL()(hoặc một phương thức tương tự) để có được một biểu diễn nhị phân của kết quả.Băm mã. Chuỗi (hoặc mảng pixel) thu được được đưa qua một hàm băm. Mã hash kết quả được gửi tới máy chủ và được sử dụng làm vân tay.
Để dễ hiểu hơn: hãy vẽ từ "Vân tay" trên một Canvas và tính toán một mã hash cơ bản:
let canvas = document.createElement("canvas"); let ctx = canvas.getContext("2d"); canvas.width = 200; canvas.height = 50; ctx.textBaseline = "top"; ctx.font = "20px Arial"; ctx.fillStyle = "#f60"; ctx.fillText("Fingerprint", 10, 10); let data = canvas.toDataURL(); let hash = 0; for (let i = 0; i < data.length; i++) { hash = (hash << 5) - hash + data.charCodeAt(i); hash |= 0; } console.log("Canvas hash:", hash);
Mã này sẽ tạo ra một số 32-bit (nó có thể âm do tràn) phụ thuộc vào việc trình duyệt hiển thị văn bản như thế nào. Với một trình duyệt khác (trong ví dụ của chúng ta, Opera) kết quả có thể khác nhau (dù đôi khi trùng hợp, điều này khá hiếm). Trên một PC khác, kết quả càng có khả năng khác nhau hơn, mặc dù sự trùng khớp có thể xảy ra trong các trường hợp đặc biệt.
Các ảnh chụp màn hình dưới đây cho thấy rằng thậm chí trên cùng một PC nhưng trong các trình duyệt khác nhau mã hash có sự khác biệt: trong trường hợp đầu tiên chúng tôi hiển thị từ "Vân tay" trong Chrome, trong trường hợp thứ hai là Opera. Bạn có thể lặp lại điều này trên chính thiết bị của mình.


Tại sao chúng khác nhau? Không phải vì văn bản “Vân tay” tự nó: về mặt hình ảnh nó trông giống như vậy đối với mắt người trên các thiết bị khác nhau. Chênh lệch xuất hiện ở cấp độ hiển thị: gợi ý phông chữ, làm mịn đồ họa, các thuật toán raster hóa và điều chỉnh nhẹ ký tự cho lưới pixel được xử lý khác nhau bởi các hệ điều hành và trình duyệt khác nhau. Thêm vào sự khác biệt về GPU và trình điều khiển, và mỗi thiết bị đưa ra những méo mó nhỏ trong hình ảnh cuối cùng. Trên một pixel, cạnh chữ cái có thể hơi sáng hơn, trên một pixel khác thì đậm hơn, ở đâu đó ký tự được làm mượt khác nhau. Những khác biệt vi mô này dẫn đến các mã hash khác nhau mặc dù hình ảnh trông giống nhau.
Các trang web cố gắng tăng cường mức độ thay đổi. Chúng có thể sử dụng các chuỗi đặc biệt được chế tác gồm toàn bộ bảng chữ cái và các ký hiệu đa dạng để kích hoạt nhiều đường dẫn mã hiển thị; ví dụ, một câu bao gồm hầu hết các chữ cái Latin: “Cwm fjordbank glyphs mute quiz.” Chúng cũng có thể vẽ các hình chữ nhật màu, chuyển màu, hoặc cái bóng lên trên văn bản - tất cả những gì để trích xuất nhiểu chi tiết độc đáo ở mức độ pixel. Kết quả cuối cùng của vân tay Canvas là chuỗi mã hash đã đề cập trước đó, nhưng phức tạp hơn (ví dụ, e3d52382d0…). Mã băm này nhất quán xác định thiết bị đó và, khi truy cập lại, cùng một trình duyệt sẽ tạo ra cùng một mã hash trừ khi môi trường của nó thay đổi.
Do đó, Canvas đem lại cho các trang web một công cụ theo dõi mạnh mẽ. Không cần sự cho phép của người dùng, trang web thu thập một "bản render" phụ thuộc phần cứng của hệ thống. Các thiết bị có GPU và phần mềm giống hệt nhau có thể tạo ra vân tay Canvas giống nhau, nhưng tìm hai bản sao như vậy là cực kỳ hiếm. Thông thường sự kết hợp của GPU cụ thể, phông chữ và phần mềm đủ độc đáo để theo dõi.
Vân tay Ngữ cảnh âm thanh
Phương pháp tiếp theo là vân tay âm thanh, nơi Web Audio API trở thành nguồn của một “âm thanh” nhận dạng riêng biệt. Nghe có vẻ lạ: trang web không yêu cầu quyền truy cập micro và không phát âm thanh có thể nghe được. Nó tinh tế hơn. Kịch bản tạo ra và xử lý tín hiệu âm thanh bên trong trình duyệt và sau đó trích xuất các chỉ số số hóa gián tiếp phản ánh các đặc điểm của hệ thống. Kết quả là một nhận dạng ổn định tương tự như mã Canvas, nhưng dựa trên âm thanh.
Cách nó hoạt động trong nét rộng:
Ngữ cảnh âm thanh. Kịch bản tạo ra một ngữ cảnh âm thanh ẩn (thường là một
OfflineAudioContext) - một “card âm thanh” ảo có thể xử lý âm thanh trong bộ nhớ mà không cần xuất ra loa.Tạo tín hiệu. Một bộ dao động (
OscillatorNode) tạo ra một âm thanh có tần số cố định (ví dụ, 1,000 Hz cho một sóng tam giác). Thay vì tải một tệp âm thanh, bộ dao động tổng hợp âm thanh được lập trình.Xử lý hiệu ứng. Để phóng đại sự khác biệt phần cứng, tín hiệu được định tuyến qua các hiệu ứng âm thanh - thường là một bộ nén (
DynamicsCompressorNode) “nén” dạng sóng. Bằng cách cấu hình ngưỡng, tỷ lệ, thời gian nhả và các thông số khác, các thay đổi tinh tế của dạng sóng xuất hiện.Hiển thị và đọc. Ngữ cảnh âm thanh ảo nhanh chóng hiển thị đoạn âm thanh đã chỉ định trong bộ nhớ. Sau khi vẽ, kịch bản thu được một bộ đệm các giá trị mẫu (một mảng các số float). Ví dụ, ở 44,100 Hz và ~113 ms thời lượng có thể có ~5,000 mẫu.
Tính toán vân tay. Mảng mẫu được giảm thành một số nhỏ gọn. Một phương pháp đơn giản là tổng hợp giá trị tuyệt đối của tất cả các mẫu và lấy số chữ số quan trọng nhất. Con số đó trở thành vân tay âm thanh.
Dưới đây là một ví dụ đơn giản về mã bạn có thể chạy trong bảng điều khiển trình duyệt để tạo ra một vân tay âm thanh:
(async () => { const AC = window.OfflineAudioContext || window.webkitOfflineAudioContext; const ctx = new AC(1, 5000, 44100); const osc = ctx.createOscillator(); osc.type = 'triangle'; osc.frequency.value = 1000; const comp = ctx.createDynamicsCompressor(); comp.threshold.value = -50; comp.knee.value = 40; comp.ratio.value = 12; comp.attack.value = 0; comp.release.value = 0.25; osc.connect(comp); comp.connect(ctx.destination); osc.start(0); const rendered = await ctx.startRendering(); const samples = rendered.getChannelData(0); let acc = 0; for (let i = 0; i < samples.length; i++) acc += Math.abs(samples[i]); const demo = Math.round(acc * 1e6) / 1e6; const buf = samples.buffer.slice( samples.byteOffset, samples.byteOffset + samples.byteLength ); const hashBuf = await crypto.subtle.digest('SHA-256', buf); const hashHex = Array.from(new Uint8Array(hashBuf)) .map(b => b.toString(16).padStart(2, '0')) .join(''); console.log('Audio demo sum:', demo); console.log('Audio SHA-256 :', hashHex); })();
Trên những ảnh chụp màn hình dưới đây bạn có thể thấy rằng vân tay âm thanh của cùng một PC trong các trình duyệt khác nhau không khác nhau (trong trường hợp của chúng tôi nó tạo ra con số 953.152941). Bạn có thể so sánh chúng và chạy mã trên chính PC của mình. Kết quả sẽ giống nhau, nhưng với vân tay của riêng bạn, tất nhiên.


Do đó, mỗi thiết bị thường có số riêng của mình, một mã âm thanh.
Các nhà phát triển trình duyệt từ lâu đã nhận ra sự nguy hiểm của phương pháp này. Apple là một trong những người đầu tiên thêm bảo vệ: kể từ Safari 17, trong chế độ Riêng tư, AudioContext API cố ý thêm một chút ngẫu nhiên vào âm thanh được tạo ra. Nhờ thay đổi này, cùng một Safari có thể tạo ra các mã hash âm thanh khác nhau trong các phiên khác nhau. Hầu hết các trình duyệt khác vẫn cho phép tạo mã âm thanh mà không gặp trở ngại lớn nào (như ví dụ trên chứng minh). Kết hợp với dữ liệu Canvas và WebGL, vân tay âm thanh tăng cường mạnh mẽ khả năng nhận diện thiết bị.
Vân tay WebGL
HTML5 WebGL là một API đồ họa để hiển thị 3D trong trình duyệt (thông qua <canvas> với một ngữ cảnh WebGL). Nó cũng đã trở thành một công cụ để thu thập vân tay thiết bị. Nếu vân tay Canvas tiết lộ sự khác biệt trong hiển thị 2D, WebGL đi sâu hơn, vào chính GPU. Khả năng nhận dạng ở đây thậm chí còn rộng hơn. Từ dữ liệu WebGL, bạn có thể học gần như trực tiếp về mô hình GPU và trình điều khiển, và các chi tiết nhỏ trong việc hiển thị có thể thậm chí phân biệt hai thiết bị có cùng GPU.
Một kịch bản vân tay WebGL điển hình trông như thế này:
Khởi tạo WebGL. Kịch bản tạo một ngữ cảnh WebGL (ví dụ,
canvas.getContext("webgl2")). Ở bước này, kịch bản đã có thể có được một số chi tiết về môi trường: tên GPU (nhà sản xuất/nhà cung cấp), phiên bản trình điều khiển, các phần mở rộng được hỗ trợ, v.v.Hiển thị cảnh. Sau đó, một canvas ẩn hiển thị một cảnh 3D hoặc các nguyên thủy đặc biệt. Thường thì một tập hợp các hình dạng với các hiệu ứng shader, ánh sáng và kết cấu được vẽ - đủ để thực hiện nhiều phần của chuỗi đồ họa.
Thu thập tham số. Sau khi hiển thị, kịch bản đọc hình ảnh kết quả (thông qua
gl.readPixels) và truy vấn một tập hợp các tham số WebGL: các phần mở rộng được hỗ trợ, kích thước tối đa của kết cấu, độ chính xác của shader, chuỗiRENDERER/VENDOR, v.v. Những dữ liệu này tạo thành một kiểu "chụp nhanh phần cứng" của hệ thống đồ họa.Tạo mã băm. Các con số và chuỗi được thu thập được kết hợp và băm lại (ví dụ với thuật toán SHA-256). Mã băm kết quả trở thành vân tay WebGL. Nó được gửi đến máy chủ và trang web có thể sử dụng nó để nhận dạng trong các lần truy cập hiện tại và tương lai.
Đây là một ví dụ về cách một mã băm như vậy được tạo ra:
(async () => { const cv = document.createElement('canvas'); cv.width = 400; cv.height = 200; const gl = cv.getContext('webgl2', {antialias:true}) || cv.getContext('webgl', {antialias:true}); if (!gl) { console.log('WebGL недоступен'); return; } const info = {}; const dbg = gl.getExtension('WEBGL_debug_renderer_info'); if (dbg) { info.vendor = gl.getParameter(dbg.UNMASKED_VENDOR_WEBGL); info.renderer = gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL); } else { info.vendor = '(masked)'; info.renderer = '(masked)'; } info.version = gl.getParameter(gl.VERSION); info.glsl = gl.getParameter(gl.SHADING_LANGUAGE_VERSION); info.maxTex = gl.getParameter(gl.MAX_TEXTURE_SIZE); const vs = ` attribute vec2 p; void main(){ gl_Position = vec4(p,0.0,1.0); } `; const fs = ` precision highp float; uniform vec2 u_res; float h(vec2 v){ float s = sin(dot(v, vec2(12.9898,78.233))) * 43758.5453; return fract(s); } void main(){ vec2 uv = gl_FragCoord.xy / u_res; float r = h(uv + vec2(0.11,0.21)); float g = h(uv*1.3 + vec2(0.31,0.41)); float b = h(uv*1.7 + vec2(0.51,0.61)); gl_FragColor = vec4(pow(vec3(r,g,b)*(0.6+0.4*uv.x), vec3(1.1)), 1.0); } `; function sh(type, src){ const s = gl.createShader(type); gl.shaderSource(s, src); gl.compileShader(s); if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s)||'shader error'); return s; } const pr = gl.createProgram(); gl.attachShader(pr, sh(gl.VERTEX_SHADER, vs)); gl.attachShader(pr, sh(gl.FRAGMENT_SHADER, fs)); gl.linkProgram(pr); if (!gl.getProgramParameter(pr, gl.LINK_STATUS)) throw new Error(gl.getProgramInfoLog(pr)||'link error'); gl.useProgram(pr); const buf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 3,-1, -1,3]), gl.STATIC_DRAW); const loc = gl.getAttribLocation(pr, 'p'); gl.enableVertexAttribArray(loc); gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0); const ures = gl.getUniformLocation(pr, 'u_res'); gl.uniform2f(ures, gl.drawingBufferWidth, gl.drawingBufferHeight); gl.viewport(0,0,gl.drawingBufferWidth, gl.drawingBufferHeight); gl.clearColor(0,0,0,1); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, 3); const w = gl.drawingBufferWidth, h = gl.drawingBufferHeight; const px = new Uint8Array(w*h*4); gl.readPixels(0,0,w,h, gl.RGBA, gl.UNSIGNED_BYTE, px); const enc = new TextEncoder(); const meta = enc.encode(JSON.stringify(info)); const full = new Uint8Array(meta.length + px.length); full.set(meta, 0); full.set(px, meta.length); const hashBuf = await crypto.subtle.digest('SHA-256', full.buffer); const hex = Array.from(new Uint8Array(hashBuf)) .map(b=>b.toString(16).padStart(2,'0')).join(''); let hi = 0>>>0, lo = 0>>>0; for (let i=0;i<px.length;i+=16){ const a = px[i] | (px[i+1]<<8) | (px[i+2]<<16) | (px[i+3]<<24); hi = ((hi ^ a) + 0x9e3779b9) >>> 0; lo = ((lo ^ ((a<<7)|(a>>>25))) + 0x85ebca6b) >>> 0; hi ^= (hi<<13)>>>0; lo ^= (lo<<15)>>>0; } const sample64 = ('00000000'+hi.toString(16)).slice(-8)+('00000000'+lo.toString(16)).slice(-8); console.log('WebGL vendor :', info.vendor); console.log('WebGL renderer:', info.renderer); console.log('WebGL version :', info.version, '| GLSL:', info.glsl); console.log('MAX_TEXTURE_SIZE:', info.maxTex); console.log('SHA-256(meta+pixels):', hex); console.log('Sample64:', sample64); })();
Đây là những gì chúng ta nhận được cuối cùng:

Điểm khác biệt nào mà WebGL tiết lộ? Đầu tiên, các định danh GPU. Ví dụ, Intel Graphics tích hợp và NVIDIA GeForce hoặc AMD Radeon rời rạc có khả năng khác nhau, kích thước VRAM và trình điều khiển khác nhau, điều này được phản ánh trong các tham số ngữ cảnh. Thứ hai, các biến thể trong cùng một mô hình. Thậm chí các mẫu GPU giống hệt nhau giả định có các sự khác biệt nhỏ trong hiệu suất và độ chính xác tính toán. WebGL cũng có thể lộ ra những điều đó: đo thời gian thực thi shader hoặc các hiện tượng nghịch lý về pixel trong đầu ra hiển thị sẽ phơi bày sự khác biệt. Cuối cùng, chính trình duyệt cũng quan trọng. Các động cơ hiển thị khác nhau (Blink, WebKit, Gecko) thực thi các cuộc gọi WebGL khác nhau và có thể tạo ra kết quả hiển thị hơi khác. Hình chụp màn hình dưới đây cho thấy khi chúng tôi tạo ra cùng một vân tay trong Opera, có sự khác biệt.

Vân tay WebGL thường được sử dụng cùng với Canvas và Audio để theo dõi đa tầng, nhưng ngay cả khi sử dụng một mình nó cũng cung cấp đủ thông tin và có thể tiết lộ nhiều điều về hệ thống.
Cách bảo vệ bản thân tránh vân tay trình duyệt
Hoàn toàn loại bỏ vân tay trình duyệt là cực kỳ khó, vì có quá nhiều kênh rò rỉ. Tuy nhiên, một số biện pháp có thể giảm việc bạn bị phát hiện:
Các chế độ đặc biệt và trình duyệt. Tor Browser áp dụng bảo vệ nghiêm ngặt: tất cả người dùng có đặc điểm giống nhau, như nhân bản. Canvas và WebGL bị vô hiệu hóa ở đó hoặc trả về giá trị trung bình. Điểm trừ là nhiều dịch vụ web bị gián đoạn. Brave ở chế độ bảo vệ mạnh cũng chặn các biện pháp theo dõi thông thường. Safari trong chế độ Riêng tư thêm nhiễu và dữ liệu âm thanh để che giấu vân tay.
Chặn và giả mạo. Có các tiện ích mở rộng như CanvasBlocker ngăn mã không thể đọc Canvas hoặc giả mạo hình ảnh với một cái ngẫu nhiên. Cũng có các plugin cho AudioContext. Tuy nhiên, tương đối ít người dùng sử dụng các tiện ích này (khoảng ~100k). Một trang mà thấy một Canvas hoàn toàn trống hoặc một mã hash nhiễu thay đổi điên loạn sẽ nghi ngờ điều gì đó. Thay vì ẩn giấu, bạn có thể nổi bật hơn nữa.
Thống nhất môi trường. Một cách tiếp cận khác là làm cho vân tay không còn đặc biệt mà trở nên chung chung — ví dụ, chạy trình duyệt bên trong một máy ảo hoặc dịch vụ đám mây mà tất cả các khách hàng có cùng một cấu hình. Một số hệ thống chống gian lận làm điều này: người dùng nghi ngờ được chạy bên trong một “trình duyệt giả lập” cách ly, nơi vân tay của họ được ẩn danh. Nhưng điều này rõ ràng là bất tiện cho việc duyệt web hàng ngày.
Giả mạo có kiểm soát. Một trong những giải pháp tốt nhất là các trình duyệt chống phát hiện cho phép cấu hình môi trường chi tiết. Chúng cho phép bạn quyết định Canvas hoặc WebGL nào mà trang web có thể thấy. Một ví dụ hoàn hảo là Octo Browser. Nó cung cấp giả mạo chủ động: thêm nhiễu vào Canvas và âm thanh, giả mạo WebGL và các tham số khác có thể được xác định, và tạo ra các cấu hình giống với thiết bị thật. Các trình duyệt chống phát hiện cố gắng sao chép một trình duyệt thực sự thay vì thay đổi mọi thứ ngẫu nhiên. Một trình duyệt chống phát hiện tốt làm cho mỗi cấu hình trông độc đáo một cách hợp lý mà không nổi bật giữa hàng triệu người dùng khác.
Nếu bạn muốn thực sự ẩn danh trên mạng, bạn sẽ cần các trình duyệt và thiết bị khác nhau, giữ cho phần mềm của bạn được cập nhật liên tục, vô hiệu hóa các plugin không cần thiết, v.v., nhưng ngay cả khi đó bạn chỉ cải thiện một chút cơ hội của bạn hòa mình vào đám đông. Giải pháp thay thế tốt hơn và thực tế hơn là sử dụng các công cụ chuyên nghiệp như Octo Browser. Một trình duyệt chống phát hiện có thể giả mạo vân tay của bạn một cách có ý nghĩa và nhất quán và giúp bạn bảo vệ sự riêng tư thực sự trên mạng.
Vân tay Canvas
Canvas API trong trình duyệt cho phép vẽ đồ họa thông qua JavaScript. Khả năng này được sử dụng một cách gián tiếp để lấy vân tay. Một đoạn mã trên trang tạo ra một phần tử <canvas> vô hình và thực hiện một loạt các lệnh vẽ (chẳng hạn như, hiển thị văn bản, hình học, thêm bóng), sau đó đọc hình ảnh kết quả dưới dạng mảng pixel. Mỗi thiết bị tạo ra các biến thể nhỏ trong đầu ra pixel cuối cùng do sự khác biệt trong phần cứng và phần mềm của nó. Một mã hash sau đó được tính toán từ dữ liệu đó và được sử dụng làm một nhận dạng duy nhất cho người dùng.
Quy trình có thể được miêu tả từng bước như sau:
Tạo Canvas. Trang web được ghé thăm chèn động một phần tử
<canvas>vào trang (thường bị ẩn khỏi màn hình).Vẽ. Mã JS hiển thị đồ họa kiểm tra vào canvas đó. Thông thường, nó vẽ một chuỗi văn bản bằng một phông chữ hiếm, thêm những hình dạng màu, đường nét và hiệu ứng (chuyển màu, bóng).
Đọc các pixel. Sau khi vẽ, kịch bản gọi
toDataURL()(hoặc một phương thức tương tự) để có được một biểu diễn nhị phân của kết quả.Băm mã. Chuỗi (hoặc mảng pixel) thu được được đưa qua một hàm băm. Mã hash kết quả được gửi tới máy chủ và được sử dụng làm vân tay.
Để dễ hiểu hơn: hãy vẽ từ "Vân tay" trên một Canvas và tính toán một mã hash cơ bản:
let canvas = document.createElement("canvas"); let ctx = canvas.getContext("2d"); canvas.width = 200; canvas.height = 50; ctx.textBaseline = "top"; ctx.font = "20px Arial"; ctx.fillStyle = "#f60"; ctx.fillText("Fingerprint", 10, 10); let data = canvas.toDataURL(); let hash = 0; for (let i = 0; i < data.length; i++) { hash = (hash << 5) - hash + data.charCodeAt(i); hash |= 0; } console.log("Canvas hash:", hash);
Mã này sẽ tạo ra một số 32-bit (nó có thể âm do tràn) phụ thuộc vào việc trình duyệt hiển thị văn bản như thế nào. Với một trình duyệt khác (trong ví dụ của chúng ta, Opera) kết quả có thể khác nhau (dù đôi khi trùng hợp, điều này khá hiếm). Trên một PC khác, kết quả càng có khả năng khác nhau hơn, mặc dù sự trùng khớp có thể xảy ra trong các trường hợp đặc biệt.
Các ảnh chụp màn hình dưới đây cho thấy rằng thậm chí trên cùng một PC nhưng trong các trình duyệt khác nhau mã hash có sự khác biệt: trong trường hợp đầu tiên chúng tôi hiển thị từ "Vân tay" trong Chrome, trong trường hợp thứ hai là Opera. Bạn có thể lặp lại điều này trên chính thiết bị của mình.


Tại sao chúng khác nhau? Không phải vì văn bản “Vân tay” tự nó: về mặt hình ảnh nó trông giống như vậy đối với mắt người trên các thiết bị khác nhau. Chênh lệch xuất hiện ở cấp độ hiển thị: gợi ý phông chữ, làm mịn đồ họa, các thuật toán raster hóa và điều chỉnh nhẹ ký tự cho lưới pixel được xử lý khác nhau bởi các hệ điều hành và trình duyệt khác nhau. Thêm vào sự khác biệt về GPU và trình điều khiển, và mỗi thiết bị đưa ra những méo mó nhỏ trong hình ảnh cuối cùng. Trên một pixel, cạnh chữ cái có thể hơi sáng hơn, trên một pixel khác thì đậm hơn, ở đâu đó ký tự được làm mượt khác nhau. Những khác biệt vi mô này dẫn đến các mã hash khác nhau mặc dù hình ảnh trông giống nhau.
Các trang web cố gắng tăng cường mức độ thay đổi. Chúng có thể sử dụng các chuỗi đặc biệt được chế tác gồm toàn bộ bảng chữ cái và các ký hiệu đa dạng để kích hoạt nhiều đường dẫn mã hiển thị; ví dụ, một câu bao gồm hầu hết các chữ cái Latin: “Cwm fjordbank glyphs mute quiz.” Chúng cũng có thể vẽ các hình chữ nhật màu, chuyển màu, hoặc cái bóng lên trên văn bản - tất cả những gì để trích xuất nhiểu chi tiết độc đáo ở mức độ pixel. Kết quả cuối cùng của vân tay Canvas là chuỗi mã hash đã đề cập trước đó, nhưng phức tạp hơn (ví dụ, e3d52382d0…). Mã băm này nhất quán xác định thiết bị đó và, khi truy cập lại, cùng một trình duyệt sẽ tạo ra cùng một mã hash trừ khi môi trường của nó thay đổi.
Do đó, Canvas đem lại cho các trang web một công cụ theo dõi mạnh mẽ. Không cần sự cho phép của người dùng, trang web thu thập một "bản render" phụ thuộc phần cứng của hệ thống. Các thiết bị có GPU và phần mềm giống hệt nhau có thể tạo ra vân tay Canvas giống nhau, nhưng tìm hai bản sao như vậy là cực kỳ hiếm. Thông thường sự kết hợp của GPU cụ thể, phông chữ và phần mềm đủ độc đáo để theo dõi.
Vân tay Ngữ cảnh âm thanh
Phương pháp tiếp theo là vân tay âm thanh, nơi Web Audio API trở thành nguồn của một “âm thanh” nhận dạng riêng biệt. Nghe có vẻ lạ: trang web không yêu cầu quyền truy cập micro và không phát âm thanh có thể nghe được. Nó tinh tế hơn. Kịch bản tạo ra và xử lý tín hiệu âm thanh bên trong trình duyệt và sau đó trích xuất các chỉ số số hóa gián tiếp phản ánh các đặc điểm của hệ thống. Kết quả là một nhận dạng ổn định tương tự như mã Canvas, nhưng dựa trên âm thanh.
Cách nó hoạt động trong nét rộng:
Ngữ cảnh âm thanh. Kịch bản tạo ra một ngữ cảnh âm thanh ẩn (thường là một
OfflineAudioContext) - một “card âm thanh” ảo có thể xử lý âm thanh trong bộ nhớ mà không cần xuất ra loa.Tạo tín hiệu. Một bộ dao động (
OscillatorNode) tạo ra một âm thanh có tần số cố định (ví dụ, 1,000 Hz cho một sóng tam giác). Thay vì tải một tệp âm thanh, bộ dao động tổng hợp âm thanh được lập trình.Xử lý hiệu ứng. Để phóng đại sự khác biệt phần cứng, tín hiệu được định tuyến qua các hiệu ứng âm thanh - thường là một bộ nén (
DynamicsCompressorNode) “nén” dạng sóng. Bằng cách cấu hình ngưỡng, tỷ lệ, thời gian nhả và các thông số khác, các thay đổi tinh tế của dạng sóng xuất hiện.Hiển thị và đọc. Ngữ cảnh âm thanh ảo nhanh chóng hiển thị đoạn âm thanh đã chỉ định trong bộ nhớ. Sau khi vẽ, kịch bản thu được một bộ đệm các giá trị mẫu (một mảng các số float). Ví dụ, ở 44,100 Hz và ~113 ms thời lượng có thể có ~5,000 mẫu.
Tính toán vân tay. Mảng mẫu được giảm thành một số nhỏ gọn. Một phương pháp đơn giản là tổng hợp giá trị tuyệt đối của tất cả các mẫu và lấy số chữ số quan trọng nhất. Con số đó trở thành vân tay âm thanh.
Dưới đây là một ví dụ đơn giản về mã bạn có thể chạy trong bảng điều khiển trình duyệt để tạo ra một vân tay âm thanh:
(async () => { const AC = window.OfflineAudioContext || window.webkitOfflineAudioContext; const ctx = new AC(1, 5000, 44100); const osc = ctx.createOscillator(); osc.type = 'triangle'; osc.frequency.value = 1000; const comp = ctx.createDynamicsCompressor(); comp.threshold.value = -50; comp.knee.value = 40; comp.ratio.value = 12; comp.attack.value = 0; comp.release.value = 0.25; osc.connect(comp); comp.connect(ctx.destination); osc.start(0); const rendered = await ctx.startRendering(); const samples = rendered.getChannelData(0); let acc = 0; for (let i = 0; i < samples.length; i++) acc += Math.abs(samples[i]); const demo = Math.round(acc * 1e6) / 1e6; const buf = samples.buffer.slice( samples.byteOffset, samples.byteOffset + samples.byteLength ); const hashBuf = await crypto.subtle.digest('SHA-256', buf); const hashHex = Array.from(new Uint8Array(hashBuf)) .map(b => b.toString(16).padStart(2, '0')) .join(''); console.log('Audio demo sum:', demo); console.log('Audio SHA-256 :', hashHex); })();
Trên những ảnh chụp màn hình dưới đây bạn có thể thấy rằng vân tay âm thanh của cùng một PC trong các trình duyệt khác nhau không khác nhau (trong trường hợp của chúng tôi nó tạo ra con số 953.152941). Bạn có thể so sánh chúng và chạy mã trên chính PC của mình. Kết quả sẽ giống nhau, nhưng với vân tay của riêng bạn, tất nhiên.


Do đó, mỗi thiết bị thường có số riêng của mình, một mã âm thanh.
Các nhà phát triển trình duyệt từ lâu đã nhận ra sự nguy hiểm của phương pháp này. Apple là một trong những người đầu tiên thêm bảo vệ: kể từ Safari 17, trong chế độ Riêng tư, AudioContext API cố ý thêm một chút ngẫu nhiên vào âm thanh được tạo ra. Nhờ thay đổi này, cùng một Safari có thể tạo ra các mã hash âm thanh khác nhau trong các phiên khác nhau. Hầu hết các trình duyệt khác vẫn cho phép tạo mã âm thanh mà không gặp trở ngại lớn nào (như ví dụ trên chứng minh). Kết hợp với dữ liệu Canvas và WebGL, vân tay âm thanh tăng cường mạnh mẽ khả năng nhận diện thiết bị.
Vân tay WebGL
HTML5 WebGL là một API đồ họa để hiển thị 3D trong trình duyệt (thông qua <canvas> với một ngữ cảnh WebGL). Nó cũng đã trở thành một công cụ để thu thập vân tay thiết bị. Nếu vân tay Canvas tiết lộ sự khác biệt trong hiển thị 2D, WebGL đi sâu hơn, vào chính GPU. Khả năng nhận dạng ở đây thậm chí còn rộng hơn. Từ dữ liệu WebGL, bạn có thể học gần như trực tiếp về mô hình GPU và trình điều khiển, và các chi tiết nhỏ trong việc hiển thị có thể thậm chí phân biệt hai thiết bị có cùng GPU.
Một kịch bản vân tay WebGL điển hình trông như thế này:
Khởi tạo WebGL. Kịch bản tạo một ngữ cảnh WebGL (ví dụ,
canvas.getContext("webgl2")). Ở bước này, kịch bản đã có thể có được một số chi tiết về môi trường: tên GPU (nhà sản xuất/nhà cung cấp), phiên bản trình điều khiển, các phần mở rộng được hỗ trợ, v.v.Hiển thị cảnh. Sau đó, một canvas ẩn hiển thị một cảnh 3D hoặc các nguyên thủy đặc biệt. Thường thì một tập hợp các hình dạng với các hiệu ứng shader, ánh sáng và kết cấu được vẽ - đủ để thực hiện nhiều phần của chuỗi đồ họa.
Thu thập tham số. Sau khi hiển thị, kịch bản đọc hình ảnh kết quả (thông qua
gl.readPixels) và truy vấn một tập hợp các tham số WebGL: các phần mở rộng được hỗ trợ, kích thước tối đa của kết cấu, độ chính xác của shader, chuỗiRENDERER/VENDOR, v.v. Những dữ liệu này tạo thành một kiểu "chụp nhanh phần cứng" của hệ thống đồ họa.Tạo mã băm. Các con số và chuỗi được thu thập được kết hợp và băm lại (ví dụ với thuật toán SHA-256). Mã băm kết quả trở thành vân tay WebGL. Nó được gửi đến máy chủ và trang web có thể sử dụng nó để nhận dạng trong các lần truy cập hiện tại và tương lai.
Đây là một ví dụ về cách một mã băm như vậy được tạo ra:
(async () => { const cv = document.createElement('canvas'); cv.width = 400; cv.height = 200; const gl = cv.getContext('webgl2', {antialias:true}) || cv.getContext('webgl', {antialias:true}); if (!gl) { console.log('WebGL недоступен'); return; } const info = {}; const dbg = gl.getExtension('WEBGL_debug_renderer_info'); if (dbg) { info.vendor = gl.getParameter(dbg.UNMASKED_VENDOR_WEBGL); info.renderer = gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL); } else { info.vendor = '(masked)'; info.renderer = '(masked)'; } info.version = gl.getParameter(gl.VERSION); info.glsl = gl.getParameter(gl.SHADING_LANGUAGE_VERSION); info.maxTex = gl.getParameter(gl.MAX_TEXTURE_SIZE); const vs = ` attribute vec2 p; void main(){ gl_Position = vec4(p,0.0,1.0); } `; const fs = ` precision highp float; uniform vec2 u_res; float h(vec2 v){ float s = sin(dot(v, vec2(12.9898,78.233))) * 43758.5453; return fract(s); } void main(){ vec2 uv = gl_FragCoord.xy / u_res; float r = h(uv + vec2(0.11,0.21)); float g = h(uv*1.3 + vec2(0.31,0.41)); float b = h(uv*1.7 + vec2(0.51,0.61)); gl_FragColor = vec4(pow(vec3(r,g,b)*(0.6+0.4*uv.x), vec3(1.1)), 1.0); } `; function sh(type, src){ const s = gl.createShader(type); gl.shaderSource(s, src); gl.compileShader(s); if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s)||'shader error'); return s; } const pr = gl.createProgram(); gl.attachShader(pr, sh(gl.VERTEX_SHADER, vs)); gl.attachShader(pr, sh(gl.FRAGMENT_SHADER, fs)); gl.linkProgram(pr); if (!gl.getProgramParameter(pr, gl.LINK_STATUS)) throw new Error(gl.getProgramInfoLog(pr)||'link error'); gl.useProgram(pr); const buf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 3,-1, -1,3]), gl.STATIC_DRAW); const loc = gl.getAttribLocation(pr, 'p'); gl.enableVertexAttribArray(loc); gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0); const ures = gl.getUniformLocation(pr, 'u_res'); gl.uniform2f(ures, gl.drawingBufferWidth, gl.drawingBufferHeight); gl.viewport(0,0,gl.drawingBufferWidth, gl.drawingBufferHeight); gl.clearColor(0,0,0,1); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, 3); const w = gl.drawingBufferWidth, h = gl.drawingBufferHeight; const px = new Uint8Array(w*h*4); gl.readPixels(0,0,w,h, gl.RGBA, gl.UNSIGNED_BYTE, px); const enc = new TextEncoder(); const meta = enc.encode(JSON.stringify(info)); const full = new Uint8Array(meta.length + px.length); full.set(meta, 0); full.set(px, meta.length); const hashBuf = await crypto.subtle.digest('SHA-256', full.buffer); const hex = Array.from(new Uint8Array(hashBuf)) .map(b=>b.toString(16).padStart(2,'0')).join(''); let hi = 0>>>0, lo = 0>>>0; for (let i=0;i<px.length;i+=16){ const a = px[i] | (px[i+1]<<8) | (px[i+2]<<16) | (px[i+3]<<24); hi = ((hi ^ a) + 0x9e3779b9) >>> 0; lo = ((lo ^ ((a<<7)|(a>>>25))) + 0x85ebca6b) >>> 0; hi ^= (hi<<13)>>>0; lo ^= (lo<<15)>>>0; } const sample64 = ('00000000'+hi.toString(16)).slice(-8)+('00000000'+lo.toString(16)).slice(-8); console.log('WebGL vendor :', info.vendor); console.log('WebGL renderer:', info.renderer); console.log('WebGL version :', info.version, '| GLSL:', info.glsl); console.log('MAX_TEXTURE_SIZE:', info.maxTex); console.log('SHA-256(meta+pixels):', hex); console.log('Sample64:', sample64); })();
Đây là những gì chúng ta nhận được cuối cùng:

Điểm khác biệt nào mà WebGL tiết lộ? Đầu tiên, các định danh GPU. Ví dụ, Intel Graphics tích hợp và NVIDIA GeForce hoặc AMD Radeon rời rạc có khả năng khác nhau, kích thước VRAM và trình điều khiển khác nhau, điều này được phản ánh trong các tham số ngữ cảnh. Thứ hai, các biến thể trong cùng một mô hình. Thậm chí các mẫu GPU giống hệt nhau giả định có các sự khác biệt nhỏ trong hiệu suất và độ chính xác tính toán. WebGL cũng có thể lộ ra những điều đó: đo thời gian thực thi shader hoặc các hiện tượng nghịch lý về pixel trong đầu ra hiển thị sẽ phơi bày sự khác biệt. Cuối cùng, chính trình duyệt cũng quan trọng. Các động cơ hiển thị khác nhau (Blink, WebKit, Gecko) thực thi các cuộc gọi WebGL khác nhau và có thể tạo ra kết quả hiển thị hơi khác. Hình chụp màn hình dưới đây cho thấy khi chúng tôi tạo ra cùng một vân tay trong Opera, có sự khác biệt.

Vân tay WebGL thường được sử dụng cùng với Canvas và Audio để theo dõi đa tầng, nhưng ngay cả khi sử dụng một mình nó cũng cung cấp đủ thông tin và có thể tiết lộ nhiều điều về hệ thống.
Cách bảo vệ bản thân tránh vân tay trình duyệt
Hoàn toàn loại bỏ vân tay trình duyệt là cực kỳ khó, vì có quá nhiều kênh rò rỉ. Tuy nhiên, một số biện pháp có thể giảm việc bạn bị phát hiện:
Các chế độ đặc biệt và trình duyệt. Tor Browser áp dụng bảo vệ nghiêm ngặt: tất cả người dùng có đặc điểm giống nhau, như nhân bản. Canvas và WebGL bị vô hiệu hóa ở đó hoặc trả về giá trị trung bình. Điểm trừ là nhiều dịch vụ web bị gián đoạn. Brave ở chế độ bảo vệ mạnh cũng chặn các biện pháp theo dõi thông thường. Safari trong chế độ Riêng tư thêm nhiễu và dữ liệu âm thanh để che giấu vân tay.
Chặn và giả mạo. Có các tiện ích mở rộng như CanvasBlocker ngăn mã không thể đọc Canvas hoặc giả mạo hình ảnh với một cái ngẫu nhiên. Cũng có các plugin cho AudioContext. Tuy nhiên, tương đối ít người dùng sử dụng các tiện ích này (khoảng ~100k). Một trang mà thấy một Canvas hoàn toàn trống hoặc một mã hash nhiễu thay đổi điên loạn sẽ nghi ngờ điều gì đó. Thay vì ẩn giấu, bạn có thể nổi bật hơn nữa.
Thống nhất môi trường. Một cách tiếp cận khác là làm cho vân tay không còn đặc biệt mà trở nên chung chung — ví dụ, chạy trình duyệt bên trong một máy ảo hoặc dịch vụ đám mây mà tất cả các khách hàng có cùng một cấu hình. Một số hệ thống chống gian lận làm điều này: người dùng nghi ngờ được chạy bên trong một “trình duyệt giả lập” cách ly, nơi vân tay của họ được ẩn danh. Nhưng điều này rõ ràng là bất tiện cho việc duyệt web hàng ngày.
Giả mạo có kiểm soát. Một trong những giải pháp tốt nhất là các trình duyệt chống phát hiện cho phép cấu hình môi trường chi tiết. Chúng cho phép bạn quyết định Canvas hoặc WebGL nào mà trang web có thể thấy. Một ví dụ hoàn hảo là Octo Browser. Nó cung cấp giả mạo chủ động: thêm nhiễu vào Canvas và âm thanh, giả mạo WebGL và các tham số khác có thể được xác định, và tạo ra các cấu hình giống với thiết bị thật. Các trình duyệt chống phát hiện cố gắng sao chép một trình duyệt thực sự thay vì thay đổi mọi thứ ngẫu nhiên. Một trình duyệt chống phát hiện tốt làm cho mỗi cấu hình trông độc đáo một cách hợp lý mà không nổi bật giữa hàng triệu người dùng khác.
Nếu bạn muốn thực sự ẩn danh trên mạng, bạn sẽ cần các trình duyệt và thiết bị khác nhau, giữ cho phần mềm của bạn được cập nhật liên tục, vô hiệu hóa các plugin không cần thiết, v.v., nhưng ngay cả khi đó bạn chỉ cải thiện một chút cơ hội của bạn hòa mình vào đám đông. Giải pháp thay thế tốt hơn và thực tế hơn là sử dụng các công cụ chuyên nghiệp như Octo Browser. Một trình duyệt chống phát hiện có thể giả mạo vân tay của bạn một cách có ý nghĩa và nhất quán và giúp bạn bảo vệ sự riêng tư thực sự trên mạng.
Cập nhật với các tin tức Octo Browser mới nhất
Khi nhấp vào nút này, bạn sẽ đồng ý với Chính sách Quyền riêng tư của chúng tôi.
Cập nhật với các tin tức Octo Browser mới nhất
Khi nhấp vào nút này, bạn sẽ đồng ý với Chính sách Quyền riêng tư của chúng tôi.
Cập nhật với các tin tức Octo Browser mới nhất
Khi nhấp vào nút này, bạn sẽ đồng ý với Chính sách Quyền riêng tư của chúng tôi.
Các bài viết liên quan
Các bài viết liên quan
Các bài viết liên quan

Tham gia Octo Browser ngay
Hoặc liên hệ với Dịch vụ khách hàng bất kì lúc nào nếu bạn có bất cứ thắc mắc nào.

Tham gia Octo Browser ngay
Hoặc liên hệ với Dịch vụ khách hàng bất kì lúc nào nếu bạn có bất cứ thắc mắc nào.
Tham gia Octo Browser ngay
Hoặc liên hệ với Dịch vụ khách hàng bất kì lúc nào nếu bạn có bất cứ thắc mắc nào.


