Font Enumeration Fingerprinting

Background

Font enumeration fingerprinting is a basic fingerprint technique that started to appear in 2007. Early on this was done by not only JavaScript but Flash as well. With the decline of Flash the market has shifted towards JavaScript enumeration only.


How It Works

The installed fonts on a system vary over time based on software that has been installed in the system. The idea be­hind the JavaScript code is very sim­ple. It cre­ates a span el­e­ment with a known font fam­ily, mea­sure its width, set the tar­get font fam­ily, and mea­sure the width again. If there is a dif­fer­ence in the width, you will know that the font has ren­dered and is thus avail­able. It then comes down to creating a list of fonts to check. The list provided balances comprehensiveness against speed, as checking each font takes time.


Entropy Estimate: 13.9 bits


Code

The JavaScript function below enumerates all forms and form fields. 

Source Code

function fingerprint_fonts() {

    "use strict";

    var strOnError, style, fonts, count, template, fragment, divs, i, font, div, body, result, e;

 

    strOnError = "Error";

    style = null;

    fonts = null;

    font = null;

    count = 0;

    template = null;

    divs = null;

    e = null;

    div = null;

    body = null;

    i = 0;

 

    try {

        style = "position: absolute; visibility: hidden; display: block !important";

        fonts = ["Abadi MT Condensed Light", "Adobe Fangsong Std", "Adobe Hebrew", "Adobe Ming Std", "Agency FB", "Aharoni", "Andalus", "Angsana New", "AngsanaUPC", "Aparajita", "Arab", "Arabic Transparent", "Arabic Typesetting", "Arial Baltic", "Arial Black", "Arial CE", "Arial CYR", "Arial Greek", "Arial TUR", "Arial", "Batang", "BatangChe", "Bauhaus 93", "Bell MT", "Bitstream Vera Serif", "Bodoni MT", "Bookman Old Style", "Braggadocio", "Broadway", "Browallia New", "BrowalliaUPC", "Calibri Light", "Calibri", "Californian FB", "Cambria Math", "Cambria", "Candara", "Castellar", "Casual", "Centaur", "Century Gothic", "Chalkduster", "Colonna MT", "Comic Sans MS", "Consolas", "Constantia", "Copperplate Gothic Light", "Corbel", "Cordia New", "CordiaUPC", "Courier New Baltic", "Courier New CE", "Courier New CYR", "Courier New Greek", "Courier New TUR", "Courier New", "DFKai-SB", "DaunPenh", "David", "DejaVu LGC Sans Mono", "Desdemona", "DilleniaUPC", "DokChampa", "Dotum", "DotumChe", "Ebrima", "Engravers MT", "Eras Bold ITC", "Estrangelo Edessa", "EucrosiaUPC", "Euphemia", "Eurostile", "FangSong", "Forte", "FrankRuehl", "Franklin Gothic Heavy", "Franklin Gothic Medium", "FreesiaUPC", "French Script MT", "Gabriola", "Gautami", "Georgia", "Gigi", "Gisha", "Goudy Old Style", "Gulim", "GulimChe", "GungSeo", "Gungsuh", "GungsuhChe", "Haettenschweiler", "Harrington", "Hei S", "HeiT", "Heisei Kaku Gothic", "Hiragino Sans GB", "Impact", "Informal Roman", "IrisUPC", "Iskoola Pota", "JasmineUPC", "KacstOne", "KaiTi", "Kalinga", "Kartika", "Khmer UI", "Kino MT", "KodchiangUPC", "Kokila", "Kozuka Gothic Pr6N", "Lao UI", "Latha", "Leelawadee", "Levenim MT", "LilyUPC", "Lohit Gujarati", "Loma", "Lucida Bright", "Lucida Console", "Lucida Fax", "Lucida Sans Unicode", "MS Gothic", "MS Mincho", "MS PGothic", "MS PMincho", "MS Reference Sans Serif", "MS UI Gothic", "MV Boli", "Magneto", "Malgun Gothic", "Mangal", "Marlett", "Matura MT Script Capitals", "Meiryo UI", "Meiryo", "Menlo", "Microsoft Himalaya", "Microsoft JhengHei", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Sans Serif", "Microsoft Tai Le", "Microsoft Uighur", "Microsoft YaHei", "Microsoft Yi Baiti", "MingLiU", "MingLiU-ExtB", "MingLiU_HKSCS", "MingLiU_HKSCS-ExtB", "Miriam Fixed", "Miriam", "Mongolian Baiti", "MoolBoran", "NSimSun", "Narkisim", "News Gothic MT", "Niagara Solid", "Nyala", "PMingLiU", "PMingLiU-ExtB", "Palace Script MT", "Palatino Linotype", "Papyrus", "Perpetua", "Plantagenet Cherokee", "Playbill", "Prelude Bold", "Prelude Condensed Bold", "Prelude Condensed Medium", "Prelude Medium", "PreludeCompressedWGL Black", "PreludeCompressedWGL Bold", "PreludeCompressedWGL Light", "PreludeCompressedWGL Medium", "PreludeCondensedWGL Black", "PreludeCondensedWGL Bold", "PreludeCondensedWGL Light", "PreludeCondensedWGL Medium", "PreludeWGL Black", "PreludeWGL Bold", "PreludeWGL Light", "PreludeWGL Medium", "Raavi", "Rachana", "Rockwell", "Rod", "Sakkal Majalla", "Sawasdee", "Script MT Bold", "Segoe Print", "Segoe Script", "Segoe UI Light", "Segoe UI Semibold", "Segoe UI Symbol", "Segoe UI", "Shonar Bangla", "Showcard Gothic", "Shruti", "SimHei", "SimSun", "SimSun-ExtB", "Simplified Arabic Fixed", "Simplified Arabic", "Snap ITC", "Sylfaen", "Symbol", "Tahoma", "Times New Roman Baltic", "Times New Roman CE", "Times New Roman CYR", "Times New Roman Greek", "Times New Roman TUR", "Times New Roman", "TlwgMono", "Traditional Arabic", "Trebuchet MS", "Tunga", "Tw Cen MT Condensed Extra Bold", "Ubuntu", "Umpush", "Univers", "Utopia", "Utsaah", "Vani", "Verdana", "Vijaya", "Vladimir Script", "Vrinda", "Webdings", "Wide Latin", "Wingdings"];

        count = fonts.length;

        template = '<b style="display:inline !important; width:auto !important; font:normal 10px/1 \'X\',sans-serif !important">wwmmllii</b>' + '<b style="display:inline !important; width:auto !important; font:normal 10px/1 \'X\',monospace !important">wwmmllii</b>';

        fragment = document.createDocumentFragment();

        divs = [];

        for (i = 0; i < count; i = i + 1) {

            font = fonts[i];

            div = document.createElement('div');

            font = font.replace(/['"<>]/g, '');

            div.innerHTML = template.replace(/X/g, font);

            div.style.cssText = style;

            fragment.appendChild(div);

            divs.push(div);

        }

        body = document.body;

        body.insertBefore(fragment, body.firstChild);

        result = [];

        for (i = 0; i < count; i = i + 1) {

            e = divs[i].getElementsByTagName('b');

            if (e[0].offsetWidth === e[1].offsetWidth) {

                result.push(fonts[i]);

            }

        }

        // do not combine these two loops, remove child will cause reflow

        // and induce severe performance hit

        for (i = 0; i < count; i = i + 1) {

            body.removeChild(divs[i]);

        }

        return result.join('|');

    } catch (err) {

        return strOnError;

    }

}

Validation

Unlike other code on the Internet we do everything possible to verify our code for you. In order to minimize problems and maximize compatibility this code has been verified with JSLint and has been extensively tested with over 1100 OS/Browser combinations using BrowserStack.


Reference

Listing all fonts on a user's computer (Apr 08, 2005). In TEK-TIPS.com. Retrieved September 09, 2017, from http://www.tek-tips.com/faqs.cfm?fid=5799


JavaScript/CSS Font Detector (Mar 15, 2007). In lalit.lab. Retrieved September 09, 2017, from http://www.lalit.org/lab/javascript-css-font-detect/


Detecting System Fonts without Flash (Jul 30, 2015). In BramStein.com. Retrieved September 09, 2017, from https://www.bramstein.com/writing/detecting-system-fonts-without-flash.html