Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the available screen resolution component #585

Merged
merged 9 commits into from
Dec 28, 2020

Conversation

Finesse
Copy link
Member

@Finesse Finesse commented Nov 16, 2020

Resolves #568

  • The component is disabled in browsers where it's unstable or gives no information. It's enabled in: Chrome ≤ 83 on Windows, Firefox on Windows, IE, pre-chromium Edge, Chrome 57–78 on macOS, Safari ≤11 on macOS. Approximate share of such users is 9.3% and it decreases. See raw research data at Available screen resolution changes when in fullscreen mode on MacOS #568 (comment).
  • Top-left corner position of available screen is added.
  • The dimensions are rounded by 10 pixels to mitigate small random changes of the position (e.g. 1900x1440 → 1900x1439, the reason and steps to reproduce are unknown).

This PR will change every visitor identifier.

@Finesse Finesse self-assigned this Nov 16, 2020
@spyjo
Copy link

spyjo commented Nov 16, 2020

Hello,
How many users are using fullscreen mode ? I guess less than 1%
If I understand well, you want to replace a component which works for 99% of users, to a component which work for only 9.3% of users and decreasing, and moreover changing all visitor ID ? Please correct me if I'm wrong.

@Valve
Copy link
Member

Valve commented Nov 16, 2020

@Finesse I think this change and its consequences are more nuanced and we shouldn't hurry with merging it.
Let's discuss it first.

@Finesse
Copy link
Member Author

Finesse commented Nov 16, 2020

@spyjo Only 40% of visitors use desktop devices. This component gives no information on 99.5% of mobile devices and spoils fingerprint on the rest 0.5% of mobile devices so removing it for mobile devices will only increase accuracy a bit.

Fullscreen is very common on macOS, it's very convenient, but the macOS share is pretty small (about 3% of desktops). I also suppose that Windows users use OS fullscreen (F11) very rarely, and since such fullscreen doesn't change the component result, so there is no point in discussing this. But programmatic fullscreen does change the result in Blink (Chromium-like browsers) and programmatic fullscreen is often used on some websites to show videos, photos, presentations, etc. There are a few options to handle this case: either ask library users to not run the library in programmatic fullscreen mode, or disable the component in Blink on Windows, or complicate the library API by adding a special setting for those who's going to use programmatic fullscreen.

Update: probably programmatic fullscreen won't change the component result if the code runs in an iframe, I will check it.

@Finesse Finesse marked this pull request as draft November 16, 2020 14:02
@Finesse
Copy link
Member Author

Finesse commented Nov 17, 2020

I've tried a few tricks to get real available screen resolution in programmatic fullscreen mode on Windows including running the code in an iframe and in a separate window. They all have failed. If you have an idea to solve it, please share.

I think that UI fullscreen is an issue on macOS and it should be considered. I think that programmatic fullscreen is an issue on all platforms and should be considered even though fingerprint is very unlikely to be taken in such situation.

Browsers can be split into following groups:

  1. Available screen resolution is always stable and gives entropy. It's about 9% of all visitors and it decreases. Browsers:
    • Chrome ≤83 on Windows
    • Firefox on Windows
    • IE
    • pre-chromium Edge
    • Chrome 57–78 on macOS
    • Safari ≤11 on macOS
  2. Available screen resolution is stable and gives entropy unless OS fullscreen is used. It's about 4.2% of all visitors. Browsers:
    • Chrome ≤56 on macOS
    • Safari ≥12 on macOS
  3. Available screen resolution is stable and gives entropy unless programmatic fullscreen is used. It's about 23% of all visitors and it increases. Browsers:
    • Chrome ≥84 on Windows
  4. Available screen resolution is stable and gives entropy unless any fullscreen is used. It's about 9% of all visitors and it increases. Browsers:
    • Chrome OS
    • Firefox on macOS
    • Chrome ≥79 on macOS
  5. Available screen resolution is unstable at all (depends on the device orientation and not only swaps the numbers). It's about 0.2% of all visitors and it decreases. Browsers:
    • Android Firefox ≤68
    • All browsers on iOS 9
  6. Available screen resolution is always equal to screen resolution and therefore gives no entropy. It's about 55%. Browsers: all other mobile browsers.

Let's say that 40% of macOS and Chrome OS users toggle OS fullscreen, and 0.5% of all users have their fingerprint calculated during programmatic fullscreen. If so, then the component precision at master branch at the moment is:

Useful entropy False negative (unexpected change of fingerprint) False positive (lost chance to distinguish visitors) Evaded false negative No entropy
9% + 4.2% * 0.6 + 23% * 0.995 + 9% * 0.6 * 0.995 ≈ 39% 4.2% * 0.4 + 23% * 0.005 + 9% * (1 - 0.6 * 0.995) + 0.2% ≈ 6% 0% 0% 55%

(the tables show shares of visitors who's prone to specific situations, not shares of results of visitor identifications)

The merge request code state at commit 21b7fdc discards the component value in all the browser groups except the 1st, so the precision is:

Useful entropy False negative False positive Evaded false negative No entropy
9% 0% 4.2% * 0.6 + 23% * 0.995 + 9% * 0.6 * 0.995 ≈ 30% 4.2% * 0.4 + 23% * 0.005 + 9% * (1 - 0.6 * 0.995) + 0.2% ≈ 6% 55%

So the error rate has increased from 6% to 30% and the error type has switched from false negative to false positive. False positive is more preferable because it can be compensated by other components and false negative can't. But I think the change is too serious. My bad that I hadn't made these estimations before implementing the solution.

Luckily, I've found a way to make the component be stable despite programmatic fullscreen. The library can get and cache available screen resolution before even FingerprintJS.load() is called and/or turn off programmatic fullscreen before running the component. It's a radical solution, but it will be used in super rare cases. Using this solution and enabling the component for the 3rd group will change the precision to:

Useful entropy False negative False positive Evaded false negative No entropy
9% + 23% ≈ 32% 0% 4.2% * 0.6 + 9% * 0.6 ≈ 8% 4.2% * 0.4 + 9% * 0.4 + 0.2% ≈ 5% 55%

I.e. the error rate will increase from 6% to 8%, but the errors type will switch from false negative to false positive. This is a good deal, I think.

@Valve @spyjo What do you think?

@Valve
Copy link
Member

Valve commented Nov 18, 2020

In my experience a false positive is a far more serious type of error than a false negative. Usually it's fine to occasionally generate multiple visitorID values per single browser, but it's a worse problem to identify multiple unrelated visitors as one visitor. For this reason I think this proposal shouldn't be implemented.
As a related thing: it also doesn't handle the position of the system toolbar (left, bottom or right), which can be detected with avail values.
Ideally we need:

  1. Detect the system toolbar position and size.
  2. Ignore available resolution.

If we can do the first item consistently somehow, we should do it.

@tbadlov
Copy link

tbadlov commented Nov 18, 2020

This is also an issue if you attach a secondary monitor to your screen resolution values.
We ended up just removing the components for now.

Firefox Native Screen
version: 3.0.3
userAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:83.0) Gecko/20100101 Firefox/83.0
getOptions: {
  "debug": true
}
visitorId: d6e7e6f8c195450bcfb80b5d83bee01b
components: {
  "osCpu": {
    "value": "Intel Mac OS X 10.15",
    "duration": 0
  },
  "languages": {
    "value": [
      [
        "en-US"
      ],
      [
        "en-US",
        "en"
      ]
    ],
    "duration": 0
  },
  "colorDepth": {
    "value": 24,
    "duration": 0
  },
  "deviceMemory": {
    "duration": 0
  },
  "screenResolution": {
    "value": [
      900,
      1440
    ],
    "duration": 1
  },
  "availableScreenResolution": {
    "value": [
      877,
      1440
    ],
    "duration": 0
  },

Firefox - Attached Monitor
version: 3.0.3
userAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:83.0) Gecko/20100101 Firefox/83.0
getOptions: {
  "debug": true
}
visitorId: f3a4175eb8112a116b637ef768477ffd
components: {
  "osCpu": {
    "value": "Intel Mac OS X 10.15",
    "duration": 0
  },
  "languages": {
    "value": [
      [
        "en-US"
      ],
      [
        "en-US",
        "en"
      ]
    ],
    "duration": 1
  },
  "colorDepth": {
    "value": 24,
    "duration": 0
  },
  "deviceMemory": {
    "duration": 0
  },
  "screenResolution": {
    "value": [
      1920,
      1080
    ],
    "duration": 0
  },
  "availableScreenResolution": {
    "value": [
      1920,
      1057
    ],
    "duration": 0
  },

@Finesse
Copy link
Member Author

Finesse commented Nov 21, 2020

In my experience a false positive is a far more serious type of error than a false negative. Usually it's fine to occasionally generate multiple visitorID values per single browser, but it's a worse problem to identify multiple unrelated visitors as one visitor. For this reason I think this proposal shouldn't be implemented.

@Valve Thanks for your opinion. Ok, I won't exclude the component. I'll implement another solution to improve its accuracy without losing the entropy: exit programmatic fullscreen and watch the available screen resolution before get() is called.

As a related thing: it also doesn't handle the position of the system toolbar (left, bottom or right), which can be detected with avail values.

It does. You can see that all 4 measurements are taken at https://github.com/fingerprintjs/fingerprintjs/pull/585/files#diff-6411a5d12f7f63f6085e73468c1d6c96b5505da2b1b2404ff1493f9e50a904f1R21

Ideally we need:

  1. Detect the system toolbar position and size.
  2. Ignore available resolution.

If we can do the first item consistently somehow, we should do it.

I think it's a good idea. I'm going to make the component do measure toolbar sizes at all 4 sides. It's more reliable because toolbar sizes are unlikely to change when the screen is rotated or the resolution changes.

This is also an issue if you attach a secondary monitor to your screen resolution values.
We ended up just removing the components for now.

@tbadlov Yes, it's a known problem, there is nothing we can do with it. Do you have an idea how to mitigate this? Consider that a visitor can have many identifiers due to various changes, you can either exclude unstable components or improve your system to handle multiple identifires.

@Finesse
Copy link
Member Author

Finesse commented Nov 22, 2020

I've found a possible reason for available screen resolution to change a bit. When macOS Dock contains too many icons to fit them all in one row, it shrinks to fit more icons, i.e. the toolbar size decreases. This changes the values of screen.avail....

This is message is for historical purposes. This feature will be mitigated by the rounding.

…nstead; watch available screen frame before calling `get()`
@Finesse Finesse force-pushed the feature/available-screen-resolution branch from 3222d52 to 11013d3 Compare November 22, 2020 07:35
@Finesse
Copy link
Member Author

Finesse commented Nov 22, 2020

I've implemented the solution that I mentioned at #585 (comment). See the latest commit for the change.

@Valve @spyjo What do you think?

Comment on lines 28 to 40
function initFingerprintJS() {
FingerprintJS.load().then(fp => {
// The FingerprintJS agent is ready.
// Get a visitor identifier when you'd like to.
fp.get().then(result => {
// We recommend to call `load` at application startup.
const fpPromise = FingerprintJS.load()

// Get a visitor identifier when you need it.
fpPromise
.then(fp => fp.get)
.then(result => {
// This is the visitor identifier:
const visitorId = result.visitorId;
console.log(visitorId);
});
});
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a big timespan between starting agent and getting an identifier increases the chance of getting the real screen frame size (the frame between screen resolution and available screen resolution).

@Valve
Copy link
Member

Valve commented Dec 10, 2020

Another idea we can explore is: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/device-height

@Finesse Finesse changed the base branch from master to next-sources December 26, 2020 03:13
@Finesse Finesse marked this pull request as ready for review December 26, 2020 03:18
@Finesse
Copy link
Member Author

Finesse commented Dec 26, 2020

@Valve I've created a separate branch for commits that change fingerprint: next-sources. Please approve this PR and I'll merge it into the branch.

@Finesse Finesse merged commit 663841c into next-sources Dec 28, 2020
@Finesse Finesse deleted the feature/available-screen-resolution branch December 28, 2020 23:32
@swilliams-a3digital
Copy link

My available resolution changed 4 times in 1 day on chrome on mac OS and I NEVER entered full screen mode. It just randomly changes by 10 to 15 pixels throughout the dday.

@Valve
Copy link
Member

Valve commented Mar 26, 2021

@swilliams-a3digital looks like an edge case to me. Please feel free to exclude it using the documentation link that @Finesse provided.

@Valve
Copy link
Member

Valve commented Mar 26, 2021

@swilliams-a3digital I'm curious, do you have your dock fixed with dynamic sizing?

@swilliams-a3digital
Copy link

I am using a mac book Pro. I don't have a monitor attached, just using the laptop screen. Display settings are set to
Resoulution : Default for Display.
I pretty much have the out of the box setup for a macbook without monitor attached.

@Valve
Copy link
Member

Valve commented Mar 26, 2021

I meant more your dock settings or your other software settings. Please ignore availableScreenResultion
if it's a concern. For extended discussions, I would like to welcome you to github discussions

@Finesse
Copy link
Member Author

Finesse commented Mar 27, 2021

@swilliams-a3digital Does your Dock size change within a day? MacOS Dock shrinks when there are too many icons to fit inside, the shrinking changes the available screen resolution. Could you please send us a couple of full screenshots: the first when availableScreenResolution has one value and the second when availableScreenResolution has another value.

@swilliams-a3digital
Copy link

Here are two screen shots:
finger-print-res-1792, 1017
finger-print-res-1792, 1120

It's the same screen, but one reports resolution of 1017 and the other 1120, and this leads to making it look like a different fingerprint.

@Finesse
Copy link
Member Author

Finesse commented Apr 5, 2021

@swilliams-a3digital Thanks for the screenshots. Your screenshots have different resolutions. You either use different monitors (the fingerprint will be different for different monitors) or have cropped the screenshots (we need full screen screenshots to investigate the issue).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Available screen resolution changes when in fullscreen mode on MacOS
6 participants