The Surprising Role of Emojis in Browser Fingerprinting

2023-09-03

This article was ported from a different blogging platform and may not display properly. 

Emojis have become an essential part of online communication. The cute and expressive pictographs help convey tone and emotion in messages. But did you know that emojis can also be used to uniquely identify browsers and devices?

In this post, I’ll explain in depth how the variances in emoji support across different platforms enables browser fingerprinting. We’ll look at techniques to detect emoji support, measure emoji sizes, and generate a fingerprint hash. I’ll also provide detailed code examples to demonstrate each concept.

How Emoji Support Varies Across Browsers and Operating Systems

While Unicode defines over 3,000 emojis, support for these varies widely. Not all platforms include fonts and rendering for every emoji. For example, Apple and Microsoft design custom emoji images for their respective operating systems. As a result, the same emoji may look slightly different on iOS vs Windows.

Browsers also handle emojis differently. Chrome, Firefox, and Safari have their own ways of drawing emojis with inconsistent colors, outlines, and sizes. So even if two browsers support the same emoji, the way it gets rendered on screen may differ.

Let’s look at a code example:

const emoji = '🤠';

This cowboy hat face emoji (U+1F920) will appear to have a brown mouth on MacOS or iOS. But on Windows, it will have a red mouth and a slightly different hat. The size may also be larger on some browsers than others.

These kinds of variance in both support and visual presentation for the same emoji create identifiable fingerprints we can detect with JavaScript.

Detecting Emoji Support in Browsers

The first step in emoji fingerprinting is to check which emojis are supported. We can test this by dynamically rendering each emoji and detecting if the actual character is displayed.

Here is a function that will check if an emoji renders properly:

function detectEmoji(emoji) {

  // Render emoji in isolated div
  const div = document.createElement('div');
  div.innerText = emoji;
  document.body.appendChild(div);

  // Check rendered text
  const renderedEmoji = div.innerText;

  // Remove div
  document.body.removeChild(div);

  // Compare
  return renderedEmoji === emoji;

}

We first render the emoji in a detached div so as not to interfere with the main page contents. Then we check the [innerText](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText?ref=blog.personaldata.info) of the div to see if the actual emoji character is displayed. If not, it means the emoji was replaced with a missing character glyph or empty box.

For example, we can test support for a unicorn emoji:

if (detectEmoji('🦄')) {
  console.log('Unicorn emoji supported!');
} else {
  console.log('Unicorn emoji NOT supported!');
}

By running this check for a range of less common emojis, we can detect which ones are supported or not on a specific platform. Different browsers and operating systems will return different results.

Measuring Emoji Sizes

In addition to checking for emoji support, we can also detect differences in how emojis are rendered by measuring their on-screen size.

Even if two platforms support the same emoji, there may be minute differences in color, outlines, pixel boundaries, and size when rendered. We can detect these variances by comparing the client rectangle dimensions of an emoji across browsers.

Here is a function to measure the rendered size of an emoji:

function getEmojiSize(emoji) {

  // Render emoji invisible
  const div = document.createElement('div');
  div.innerText = emoji;
  div.style.position = 'absolute';
  div.style.visibility = 'hidden';

  // Append to document
  document.body.appendChild(div);

  // Get bounding rectangle
  const clientRect = div.getBoundingClientRect();

  // Remove div
  document.body.removeChild(div);

  // Return size
  return {
    width: clientRect.width,
    height: clientRect.height
  };

}

This dynamically renders the emoji in an invisible div to avoid disrupting the page. We then use getBoundingClientRect() to measure the precise width and height the emoji was rendered at.

For example, we can measure the size of a unicorn emoji:

const unicornSize = getEmojiSize('🦄');

console.log(unicornSize);
// { width: 20, height: 28 }

The dimensions will differ across operating systems and browsers, allowing us to detect the subtle variances.

Generating a Fingerprint Hash

Now we can combine emoji support checks with size measurements to generate a fingerprint for a browser.

The full process would look like:

  1. Define a list of emojis to test
  2. Check which emojis are supported
  3. Measure the size of supported emojis
  4. Build a fingerprint array with support and size data
  5. Hash the fingerprint into a unique identifier

Here is an example implementation:

// List of emojis
const emojis = ['🤠', '🦄', '☕', '🍕', ...];

// Generate fingerprint
function getEmojiFingerprint() {

  const fingerprint = [];

  // Check each emoji
  emojis.forEach(emoji => {

    // Detect support
    const isSupported = detectEmoji(emoji);

    if (isSupported) {

      // Measure size if supported
      const { width, height } = getEmojiSize(emoji);

      // Add to fingerprint
      fingerprint.push({
        emoji,
        width,
        height
      });

    } else {

      // Add unsupported emoji
      fingerprint.push({
        emoji,
        supported: false
      });

    }

  });

  return fingerprint;

}

// Generate and hash fingerprint
const emojiFingerprint = getEmojiFingerprint();
const hashedFingerprint = hash(emojiFingerprint);

The fingerprint array will contain the emoji support results plus any size measurements. This can then be hashed into a unique identifier to track a specific browser on a device.

The fingerprint will look something like:

[
  { emoji: '🤠', width: 22, height: 26 },
  { emoji: '🦄', supported: false },
  { emoji: '☕', width: 12, height: 16 },
  ...
]

By salting and hashing the fingerprint we can get a resilient identifier to track browsers across sessions without needing cookies.

Your Emoji Fingerprint

Hash: b78dd4fabcb9ff64f4a13496c44e8f28

The emoji fingerprint displayed above was generated using the techniques discussed in this post. Due to inconsistencies in emoji rendering across operating systems and browsers, your fingerprint hash will likely appear different if viewing this page on other devices.

For example, if you view this page on an iPhone running iOS, the emoji fingerprint will be unique to that platform’s font rendering and sizing. Opening the same page in a Windows browser would produce a different fingerprint hash due to alternate emoji support and presentation on that OS.

In essence, the variability of emoji display creates identifiable fingerprints that persist across separate devices and browsers. So your fingerprint here is distinctive to this particular operating system and browser combination. Testing other platforms would generate alternate hashes, demonstrating the efficacy of emoji fingerprinting for tracking and identification purposes.

Countermeasures

  • Education: A fundamental defense against emoji fingerprinting is user awareness. By understanding the techniques used for tracking, individuals can make more informed choices about their online behavior. Being mindful of the potential privacy risks associated with emoji-based tracking can empower users to take precautionary measures.
  • Privacy-Focused Web Browsers: Consider using web browsers that prioritize user privacy. Privacy-focused browsers are designed to minimize data collection and tracking. Browsers like Brave, and Tor Browser are known for their enhanced privacy features, which can help mitigate the risks associated with emoji fingerprinting.
  • Browser Extensions: Privacy-conscious browser extensions can be a valuable ally in protecting against various tracking methods, including emoji fingerprinting. These extensions often block or modify scripts that attempt to gather specific browser information. Consider using privacy-focused extensions like uBlock Origin to enhance your online security.

By combining education, privacy-focused web browsers, and browser extensions, users can proactively reduce the effectiveness of emoji-based tracking while maintaining greater control over their online privacy.

Conclusion

While emojis provide fun and engagement in online communication, they also reveal subtle details about the technology rendering them. By testing for emoji support and measuring size variances, we can take advantage of these inconsistencies to fingerprint browsers and devices.

Emoji fingerprinting demonstrates that even simple unicode characters can produce a surprising amount of identifying information. While this technique has some interesting applications, it also highlights growing privacy concerns as browser targeting and identification methods advance.