In daily life, biometric technology has been the standard of most smart phones, most of which have facial recognition, fingerprint recognition and other functions. The fingerprint recognition technology is very mature at present. But today we’re not talking about biometrics, we’re talking about browser fingerprints. Many people have a love-hate relationship with this technology. Why? Let’s take a closer look at browser fingerprints today.

What is a browser fingerprint

A browser fingerprint can track a Web browser through configuration and Settings that are visible to the Web browser. It is just like a fingerprint on your hand, but at this stage, a browser fingerprint identifies the browser.

The information used to identify the browser fingerprint can be UA, time zone, geographic location, or language. The information developed by the browser determines the accuracy of the browser fingerprint.

There is no real value to a web site in getting a browser fingerprint. What is valuable is the user information that the browser fingerprint corresponds to. As a webmaster, collecting a user’s browser fingerprint and recording the user’s actions is a valuable activity, especially for scenarios where there is no user identity.

For example, in A video website, user A, who has not registered the website, likes to browse the videos of secondary elements and records this through the browser fingerprint. Then, the user can directly push the videos of secondary elements to the browser next time. Because most of the Internet devices are private, such a push way is easy to gain the goodwill of most users, so that they become the users of the website.

The evolution of browser fingerprints

Like most technologies, browser fingerprinting didn’t evolve overnight. The existing generations of browser fingerprinting look like this:

  • The first generation is stateful, mainly focusing on the user’s cookie and evercookie, requiring the user to log in to get valid information.

  • In the second generation, the concept of browser fingerprint was introduced, which allows users to be more differentiated by increasing the characteristic values of the browser, such as UA and browser plug-in information

  • The third generation has already set its sights on people. By collecting users’ behaviors and habits, it can establish eigenvalues or even models for users, which can realize real tracking technology. However, the implementation is complicated and is still being explored.

The browser fingerprint tracking technology is currently in generation 2.5, as the issue of cross-browser fingerprint recognition is still unresolved.

Fingerprint acquisition

Entropy is the average amount of information contained in each received message. The higher the entropy, the more information can be transmitted, and the lower the entropy, the less information can be transmitted.

Browser fingerprint is synthesized from many browser features, and the information entropy of characteristic values is also different. Therefore, fingerprints are divided into basic fingerprints and advanced fingerprints.

Basic fingerprint

A basic fingerprint is a part that can be easily discovered and modified, such as HTTP headers.

{ "headers": { "Accept": "text/html,application/xhtml+xml,application/xml; Q = 0.9, image/webp image/apng, * / *; Q = 0.8, application/signed - exchange; v=b3", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "zh-CN,zh; Q = 0.9, en. Q = 0.8 ", "the Host" : "httpbin.org", "the Sec - Fetch - Mode" : "navigate", "the Sec - Fetch - Site" : "none", "the Sec - Fetch - User" : "?1", "upgrade-insecure -Requests": "1"," user-agent ": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"}Copy the code

In addition to HTTP fingerprints, there are other ways to obtain browser characteristics, such as:

  • UA per browser

  • HTTP ACCEPT header sent by the browser

  • Browser extensions/plug-ins installed in the browser, such as Quicktime, Flash, Java or Acrobat, and versions of these plug-ins

  • Fonts installed on your computer.

  • Whether the browser executes JavaScript scripts

  • Can browsers plant cookies and “super cookies”

  • Is the browser set to Do Not Track?

  • System platform (e.g. Win32, Linux x86)

  • System languages (e.g. Cn, en-US)

  • Whether the browser supports touch screen

Once you have these values, you can perform some calculations to get the specific entropy of the browser fingerprint and the uUID of the browser.

These information, like human weight, height and skin color, have a high probability of repetition and can only be used as auxiliary identification, so we need more accurate fingerprints to determine uniqueness.

Senior fingerprint

Ordinary fingerprints are not enough to distinguish between unique individuals, so advanced fingerprints are needed to narrow the range further and even generate a unique cross-browser identity.

The information used to produce a fingerprint has different weights, and those with higher entropy will have greater weight.

In the paper the Cross – Browser Fingerprinting via OS and the Hardware Level Features [yinzhicao.org/TrackingFre…

It can be seen from this paper that the weight of information entropy of time zone, screen resolution and color depth, Canvas and webGL on cross-browser fingerprint is relatively large. Here’s a look at what these advanced fingerprints contain.

Canvas prints

Canvas is the dynamic drawing tag in HTML5, which can also be used to generate images or process images. Even if Canvas is used to draw the same element, due to the difference of system, different font rendering engines, anti-aliasing, sub-pixel rendering and other algorithms are also different. When Canvas converts the same text into pictures, the results obtained are also different.

The code is basically: render some text on the canvas, convert it with toDataURL, and get the same value even if the privacy mode is turned on.

function getCanvasFingerprint () {    
    var canvas = document.createElement('canvas');    
    var context = canvas.getContext("2d");    
    context.font = "18pt Arial";    
    context.textBaseline = "top";    
    context.fillText("Hello, user.", 2, 2);    
    return canvas.toDataURL("image/jpeg");
}
getCanvasFingerprint()
Copy the code

The process is very simple, rendering text, toDataURL is to export the content of the entire Canvas and get the value.

WebGL fingerprint

WebGL (Web Graphics Library) is a JavaScript API that renders high-performance interactive 3D and 2D graphics in any compatible Web browser without the need for plug-ins. WebGL does this by introducing an API that is very consistent with OpenGL ES 2.0 and can be used within HTML5 elements. This consistency allows the API to take advantage of hardware graphics acceleration provided by the user device. Web sites can use WebGL to identify device fingerprints. Generally, fingerprint production can be done in two ways:

WebGL Reports – The complete WebGL browser report table is available and can be detected. In some cases, it is converted to a hash value for faster analysis.

WebGL images – Render and convert hidden 3D images to hashes. Because the end result depends on the hardware device doing the calculation, this method generates unique values for different combinations of devices and their drivers. This approach generates unique values for different device combinations and drivers.

The Browserleaks Test can be used to detect websites to see what information they can access through the API.

WebGL fingerprints are generated by first drawing a gradient object with shaders and converting the image to a Base64 string. Then enumerate all of WebGL’s extensions and capabilities and add them to a Base64 string, resulting in a huge string that may be quite unique on each device.

For example, the WebGL fingerprint production method for the Fingerprint2JS library:

Gl = getWebglCanvas() if (! gl) { return null } var result = [] var vShaderTemplate = 'attribute vec2 attrVertex; varying vec2 varyinTexCoordinate; uniform vec2 uniformOffset; void main(){varyinTexCoordinate=attrVertex+uniformOffset; Gl_Position = vec4 (attrVertex, 0, 1); }' var fShaderTemplate = 'precision mediump float; varying vec2 varyinTexCoordinate; Void main () {gl_FragColor = vec4 (varyinTexCoordinate, 0, 1); }' var vertexPosBuffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, VertexPosBuffer) var vertexPosBuffer = new Float32Array(0, 0, 0, 0, 0, 0, 0, 0, 0) 0]) // Creates and initializes the data store of the Buffer object. gl.bufferData(gl.ARRAY_BUFFER, vertices, Gl. STATIC_DRAW) vertexPosBuffer. ItemSize = 3 vertexPosBuffer. NumItems = 3 // Creates and initializes a WebGLProgram object. Var vshader = gl.createshader (gl.vertex_shader) // Configure the shader on the next two lines Gl.shadersource (vshader, vShaderTemplate) // Set the shader code gl.pileshader (vshader) // Build a shader, Var fshader = gl.createshader (gl.fragment_shader) gl.shaderSource(fshader, // Add predefined vertex shaders and fragment shaders gl.attachShader(program, attachShader) vshader) gl.attachShader(program, LinkProgram (program) // Add the defined WebGLProgram object to the current render state gl.useProgram(program) program.vertexPosAttrib = gl.getAttribLocation(program, 'attrVertex') program.offsetUniform = gl.getUniformLocation(program, 'uniformOffset') gl.enableVertexAttribArray(program.vertexPosArray) gl.vertexAttribPointer(program.vertexPosAttrib, vertexPosBuffer.itemSize, gl.FLOAT, ! 1, 0, 0) Gl. uniform2f(Program. OffsetUniform, 1, 1) gl.drawArrays(GL. TRIANGLE_STRIP, 0, 0) Gl. uniform2f(Program. vertexPosBuffer.numItems) try { result.push(gl.canvas.toDataURL()) } catch (e) { /* .toDataURL may be absent or broken (blocked by extension) */ }Copy the code

How to prevent “user fingerprints” from being generated

As mentioned earlier in this article, many people have a love-hate relationship with the browser technology. A host of websites use various techniques to “generate” their users’ fingerprints in order to make more accurate recommendations and match their browsing habits. While users enjoy the convenience brought by technology, they will inevitably feel anxious and uneasy about “privacy disclosure”. So how do we prevent “user fingerprints” from being generated?

Messing with Canvas Fingerprints

We have learned how to obtain canvas fingerprint, so how to prevent malicious acquisition? To obfuscate the Canvas fingerprint, you can just fiddle with the results from toDataURL.

ToDataURL () exports the content of the entire canvas. We need to modify part of the content in the canvas. At this time, we can copy the pixel data of the specified rectangle on the canvas by getImageData(), and then put the image data back by **putImageData()**. The images then exported using toDataURL() are different.

CanvasRenderingContext2D. GetImageData () returns a ImageData object, used to describe the Canvas area implied pixel data. This region is represented by a rectangle starting at (sx, SY), with a width of sw and a height of SH.

The ImageData interface describes an area of an element with hidden pixel data, which can be constructed by the ImageData() method, or by the creation method of a CanvasRenderingContext2D object on a canvas: CreateImageData () and getImageData ().

The ImageData object stores the real pixel data of the Canvas object and contains several read-only properties:

  • Width Width of the image, in pixels

  • Height Height of the image, in pixels

  • data

Uint8ClampedArray bit array containing RGBA integer data in the range of 0 255. It can be thought of as initial pixel data, with each pixel represented by four 1-bytes values (red, green, blue, alpha), each color value represented by a number in 0, 255. Each part is assigned to a contiguous index within an array, with the red portion of the first pixel in the upper left corner at bit 0 of the array index. Pixels are processed from left to right and top to bottom, traversing the entire array.

Unit8ClampedArray contains height and width data of 4 bytes, with indexes ranging from 0 to (WH4)-1.

For example, to read the blue part of the pixel at row 50, column 200 in the picture, then:


const blueComponent = imageData[50*(imageData.width * 4) + 200*4 + 2]
Copy the code

Here’s how to obfuscate the Canvas fingerprint:

const toBlob = HTMLCanvasElement.prototype.toBlob; const toDataURL = HTMLCanvasElement.prototype.toDataURL; HTMLCanvasElement.prototype.manipulate = function() { const {width, height} = this; // Get the CanvasRenderingContext2D const Context generated by the Canvas before toDataURL or toBlob = this.getContext('2d'); const shift = { 'r': Math.floor(Math.random() * 10) - 5, 'g': Math.floor(Math.random() * 10) - 5, 'b': Math.floor(Math.random() * 10) - 5 }; const matt = context.getImageData(0, 0, width, height); // Generate a unique image by randomly changing the values of r, G and B of each pixel in the imageData (pixel source data) generated by getImageData. for (let i = 0; i < height; i += Math.max(1, parseInt(height / 10))) { for (let j = 0; j < width; j += Math.max(1, parseInt(width / 10))) { const n = ((i * (width * 4)) + (j * 4)); matt.data[n + 0] = matt.data[n + 0] + shift.r; Mate.data [n + 1] = mate.data [n + 1] + shift.g; // Add random perturbation mate.data [n + 1] = mate.data [n + 1] + shift.g; matt.data[n + 2] = matt.data[n + 2] + shift.b; } } context.putImageData(matt, 0, 0); / / back to back. / / modify the prototype toBlob Object. DefineProperty (HTMLCanvasElement. Prototype, 'toBlob' {value: function() { if (script.dataset.active === 'true') { try { this.manipulate(); ImageData} catch(e) {console.warn('manipulation failed', e); } } return toBlob.apply(this, arguments); }}); / / modify the prototype. ToDataURL Object. DefineProperty (HTMLCanvasElement. Prototype, 'toDataURL, {value: function() { if (script.dataset.active === 'true') { try { this.manipulate(); ImageData} Catch (e) {console.warn('manipulation failed', e); } } return toDataURL.apply(this, arguments); }});Copy the code

Confuse other fingerprints

It is the same as the previous confusion of Canvas fingerprint, which is a method to change the prototype of the object to be obtained.

The confused time zone, for example, is to change the Date. The prototype. GetTimezoneOffset return values.

Ambiguity resolution is change the documentElement. ClientHeight documentElement. ClientWidth

Messing with WebGL means changing the WebGLbufferData getParameter method and so on.

Of course, there are some simple ways to prevent user fingerprints from being generated. For example, we can use a browser extension (Canvas Blocker, WebGL Fingerprint Defender, Fingerprint Spoofing, etc.) to execute a JS code before the web page loads. Change, rewrite the various functions of JS to prevent the website from getting all kinds of information, or return a fake data, in order to protect our privacy information.

Recommended reading

Go-zero: Out-of-the-box microservices framework

Say goodbye to DNS hijacking, one article read DoH