Randomizing Browser Fingerprinting Compilation: Fonts Fingerprint

JANSON

This document elaborates on font fingerprinting technology, covering its working principles, how websites utilize JavaScript to retrieve a user's font list to generate a fingerprint, methods for preventing tracking by randomizing the offsetWidth and offsetHeight of HTML elements, and also mentions other means of obtaining font fingerprints and application scenarios for web tracking.

What is a Font Fingerprint?

Font fingerprinting is a technique used for online user tracking. When you browse a website, it may use JavaScript to detect the fonts installed on your system. Because users' font installations can vary significantly due to differences in operating systems, personal preferences, or work requirements, the collected list of available fonts can be used to generate a relatively unique "fingerprint."

How is a Font Fingerprint Obtained?

First, let's understand how a website can obtain your font fingerprint using JavaScript. Simply copy the code below into the F12 console (Developer Tools) to retrieve and display your font fingerprint.

Js Copy
var baseFonts = ['monospace', 'sans-serif', 'serif'];
var testString = "mmmmmmmmmmlli";
var testSize = '72px';

var h = document.getElementsByTagName('body')[0];
var s = document.createElement('span');
s.style.fontSize = testSize;
s.innerHTML = testString;
var defaultWidth = {};
var defaultHeight = {};
for (var index in baseFonts) {
    s.style.fontFamily = baseFonts[index];
    h.appendChild(s);
    defaultWidth[baseFonts[index]] = s.offsetWidth;
    defaultHeight[baseFonts[index]] = s.offsetHeight;
    h.removeChild(s);
}

function detectFont(font) {
    var detected = false;
    for (var i = 0; i < baseFonts.length; i++) {
        s.style.fontFamily = font + ',' + baseFonts[i]; 
        h.appendChild(s);
        var matched = (s.offsetWidth != defaultWidth[baseFonts[i]] || s.offsetHeight != defaultHeight[baseFonts[i]]);
        h.removeChild(s);
        detected = detected || matched;
    }
    return detected;
}

async function sha256(message) {
    const msgBuffer = new TextEncoder().encode(message);
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    return hashHex;
}

var fonts = ["Andale Mono", "Arial", "Arial Black", "Arial Hebrew", "Arial MT", "Arial Narrow", "Arial Rounded MT Bold", "Arial Unicode MS", "Bitstream Vera Sans Mono", "Book Antiqua", "Bookman Old Style", "Calibri", "Cambria", "Cambria Math", "Century", "Century Gothic", "Century Schoolbook", "Comic Sans", "Comic Sans MS", "Consolas", "Courier", "Courier New", "Garamond", "Geneva", "Georgia", "Helvetica", "Helvetica Neue", "Impact", "Lucida Bright", "Lucida Calligraphy", "Lucida Console", "Lucida Fax", "LUCIDA GRANDE", "Lucida Handwriting", "Lucida Sans", "Lucida Sans Typewriter", "Lucida Sans Unicode", "Microsoft Sans Serif", "Monaco", "Monotype Corsiva", "MS Gothic", "MS Outlook", "MS PGothic", "MS Reference Sans Serif", "MS Sans Serif", "MS Serif", "MYRIAD", "MYRIAD PRO", "Palatino", "Palatino Linotype", "Segoe Print", "Segoe Script", "Segoe UI", "Segoe UI Light", "Segoe UI Semibold", "Segoe UI Symbol", "Tahoma", "Times", "Times New Roman", "Times New Roman PS", "Trebuchet MS", "Verdana", "Wingdings", "Wingdings 2", "Wingdings 3"];
var fontList = {};
for (var i = 0; i < fonts.length; i++) {
    var result = detectFont(fonts[i]);
    fontList[fonts[i]] = result;
}
var sFontList = JSON.stringify(fontList);
console.log('sFontList', sFontList);

sha256(sFontList).then(hash => console.log(hash));

Output Result:
d3bd8aa98d226562b16984e0d5525047887547379db35f6f7557b4831d212ca5

What is the Principle Behind Font Fingerprinting?
The technique involves rendering the text string "mmmmmmmmmmlli" and measuring its width using standard fonts (typically the browser's generic fonts: 'monospace', 'sans-serif', 'serif'). It then iterates through a list of specific fonts, measuring the width of the string when each font is applied. If a font is not installed on the system, the browser falls back to displaying the standard font. Therefore, if the measured width for a specific font matches the width of the standard font, it indicates that the font is not present on the system.

How to Compile Random Fonts Fingerprint?

1.Locate the Source Code
third_party\blink\renderer\core\html\html_element.cc

2.Add at the top (header):

c++ Copy
#include <random> 

3.Replace the existing code with:

c++ Copy
int getRandomIntForFoo2Modern() {
    static std::mt19937 generator(static_cast<unsigned long>(time(NULL)));
    std::uniform_int_distribution<int> distribution(0, 9);
    int tmp = distribution(generator);
	if (tmp > 0){
		return 0;
	}else{
		return 1;
	}
}

int HTMLElement::offsetWidthForBinding() {
  GetDocument().EnsurePaintLocationDataValidForNode(
      this, DocumentUpdateReason::kJavaScript);
  int result = 0;
  if (const auto* layout_object = GetLayoutBoxModelObject()) {
    result = AdjustedOffsetForZoom(layout_object->OffsetWidth());
    RecordScrollbarSizeForStudy(result, /* is_width= */ true,
                                /* is_offset= */ true);
  }
  result = result + getRandomIntForFoo2Modern();
  return result;
}

DISABLE_CFI_PERF
int HTMLElement::offsetHeightForBinding() {
  GetDocument().EnsurePaintLocationDataValidForNode(
      this, DocumentUpdateReason::kJavaScript);
  int result = 0;
  if (const auto* layout_object = GetLayoutBoxModelObject()) {
    result = AdjustedOffsetForZoom(layout_object->OffsetHeight());
    RecordScrollbarSizeForStudy(result, /* is_width= */ false,
                                /* is_offset= */ true);
  }
  result = result + getRandomIntForFoo2Modern();
  return result;
}
Update Time:Feb 04, 2026

Comments

Tips: Support some markdown syntax: **bold**, [bold](xxxxxxxxx), `code`, - list, > reference