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

notes #4

Open
Thorin-Oakenpants opened this issue Sep 11, 2020 · 26 comments
Open

notes #4

Thorin-Oakenpants opened this issue Sep 11, 2020 · 26 comments

Comments

@Thorin-Oakenpants
Copy link
Contributor

Thorin-Oakenpants commented Sep 11, 2020

various notes from the old repo


Promises

words of wisdom from kkapsner

That's doable but a better way would be to have eight promises - one for each test. Then you can combine them with Promise.all in all the configurations you need them. Something like this (please use better names!):

var p1 = getWorker1Promise();
...
var p5 = getWorker5Promise();
var p6 = getIframe1Promise();
...
var p8 = getIframe3Promise();

Promise.all([p1, ..., p5]).then(outputWorkerData);
Promise.all([p6, p7, p8]).then(outputIframeData);
Promise.all([p1, ...., p8]).then(combineAndCompareAll);

FP scripts


WASM

abraham:
There are ways to use WASM to interact with browser APIs, but does WASM make way to create a virtual context or scope? Iframes have their own browsing context/document environment, and Workers have their own global scope/event loop.

Here's an example of canvas fingerprinting in Rust.

kkapsner: The timings in the script are too tight for Firefox. If you set a debugger break point at line 441 it works. AND CB is protecting against it!


DOMRect / SVG

Links

Note that source

As of Firefox 76, the IntersectionObserver() constructor now accepts a Document object as its root, as well as an Element object. This lets you explicitly use a window's entire content area as the intersection bounds. We should verify that this does not allow someone to defeat the protections provided by letterboxing (probably it does not)


canvas stability

I see changes in some canvas results across FF releases. The hashes that change are

  • toDataURL + toBlob (and mozGetAsFile if applicable): they're the same hash
  • getImageData

for example: for me on windows

60
==
      3x a5ad50aa3a7411825a4c0b...
getImage e7209e6a032dddcd83f6bb...

61-71
==
      3x 4c406afafa6f85d3f152ea...
getImage 094f27144dd0e3c0a20a14...

72-73
==
      3x 98a30b2999315acb01047a...
getImage 197bbc428ccd88b6a75b3f...

74-76, Beta77, Nightly (the hash changed, not just dropping mozGetAsFile)
==
      2x 3cc435fec7e69a56f50420...
getImage 1230e7b2c39c38540940a5...

Dev77.0b9 (sometimes Beta)
==
      2x 2cc1b50af4dfc47ba1b35b...
getImage 904fc496441e05e2f41d1c...

I wonder what causes these changes

  • changes to css/layout or kerning or something: TZP is using Cwm fjordbank glyphs vext quiz, \ud83d\ude03
  • I don't think it would be anything in mozilla's actual canvas code: not the way it's jumping around in Beta/Dev

widgets

from tom: Had someone look at this, and they had the following to say:

  1. "I assume these five items are not widgets? (progress bariframe, image, datetime and text area)" - actually only iframe and image are not technically widgets.
  2. Without running a lot of tests himself, he couldn't say off-hand if the CSS is successfully leveling the playing field as asked in the "Help" section.
  3. Regarding any other widgets to add: :shorlander put together a comprehensive page with our widgets. You may want to take a look at it: http://stephenhorlander.com/form-controls.html
  4. We're working on removing native styling for our widgets and using our own, consistent styling. It would be very interesting to know whether "unstyled" widgets also suffer from fingerprinting issues. You can try this by setting the widgets to "-moz-appearance: none;", which is what :shorlander did on the left side of his page.

line breaking

Article: https://www.otsukare.info/2020/08/21/khmer-line-breaking

Can be used to?:

  • detect browser: e.g. FF vs others <-- pointless, there are so many other ways to do this
  • OS: e.g Khmer on FF doesn't wrap on Android (currently) - see web compat linked below

Pitfalls?

  • font availability : see jfkthame's comments at web compat 56316
  • zoom: be aware of it, and probably only do the test if isFF (see comments in example below)
  • perf

Notes:

  • https://github.com/w3c/sealreq/
  • Thai, Lao, and Khmer are languages that are written with no spaces between words. Spaces do occur, but they serve as phrase delimiters, rather than word delimiters.
  • quote from article (emphasis mine)
    • Line-breaking for SEAsian languages that are written without word spaces (e.g. Thai, Lao, Khmer) is based on calling an operating system API to find potential word-break positions. Hence the results are platform-dependent

example test: https://w3c.github.io/i18n-tests/css-text/line-breaking/exp-km-line-break-000.html

  • Windows: FF left, chrome right at 100% zoom
  • top and bottom examples don't line-break in FF but do in Chrome
  • increase zoom: look at the middle one
    • FF: the single line-break, stays the single
    • chrome: it creates more line-breaks
  • example
@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Sep 24, 2020

@abrahamjuliot : two questions

q1: is there a better way to do this: rather than rely on a hardcoded count of promises, how would I do a "for each promise" "for each result"

	Promise.all([
		functionA(),
		function99(),
		funtionSomethingElse(), // <-- three promises: but what if I add more
	]).then(function(result){
		for (let i=0; i < 3; i++) {  // <-- I would have to update the < number
			myarray.push(result[i])
		}
		// do stuff with myarray
	})

q2: is there a cleaner way to specify a variable: e.g. compare = chk+i

	if (i == 0) { compare = chk0
	} else if (i == 1) {compare = chk1
	} else if (i == 2) {compare = chk2
	} else if (i == 3) {compare = chk3
	}

-tia

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Sep 25, 2020

q1

let collection = [
    functionA,
    function99,
    functionA
    // add as many as you like
]

Promise.all(
    // compile array with each promise called
    collection.map(function(fn) {
        return fn()
    })
).then(function(result) {
    // for loop
    let len = promises.length
    for (let i = 0; i < len; i++) {
        myarray.push(result[i])
    }

    // or forEach 
    collection.forEach(function(currentValue, index) {
        myarray.push(result[index])
    })
})

q2

I'm terrible at naming variables. It usually takes x number of revisions till I can make sense of it. But, I like starting with a comment on what it holds or what it should do and then my goal is to find a name that makes the comment unneeded. For example, chk1 = [ ] // holds cat and mouse checks could instead be named catMouseChecks.

EDIT: I think I answered the wrong question. Let me try again...

chk+1 converts the array to a string: [1,2,3]+1 == "1,2,31". But, you could use chk[i] format if chk is an object of numeric property names, like chk = { 0: [ ], 1: [ ] }.

let chk = {
	0: [], // access with chk[0]
	1: [],
	2: [],
	3: []
}

if (i == 0) {
    compare = chk[i]
} else if (i == 1) {
    compare = chk[i]
} else if (i == 2) {
    compare = chk[i]
} else if (i == 3) {
    compare = chk[i]
}

// shorter single assignment
compare = (
	i == 0 ? chk[i] :
	i == 1 ? chk[i] :
	i == 2 ? chk[i] :
	chk[i] // i == 3
)

@kkapsner
Copy link
Collaborator

DOMRect / SVG

I'm working on that (there are some bugs in the CB code). Stay tuned.

@kkapsner
Copy link
Collaborator

kkapsner commented Nov 2, 2020

Please check the updated test page.

@Thorin-Oakenpants
Copy link
Contributor Author

Awesome: I've been wanting to dig into this for ages. TB has open tickets about some SVG items. It's all on my ToDo list. So much to do, so little time. Imma just going to keep plodding away getting existing things finished before I add more things: and really dig into your test but it looks exciting 💋 How much entropy is gained would be interesting

@Thorin-Oakenpants
Copy link
Contributor Author

@kkapsner .. this didn't affect your tests did it? I remember you saying in the old repo something about bugs and MDN documentation not matching

@kkapsner
Copy link
Collaborator

No - it was something different. The used classes did not match MDN and the bugs were in CB.

@Thorin-Oakenpants
Copy link
Contributor Author

@kkapsner I assume there's no point in looking at DOMRectReadOnly vs DOMRect

... anyway .. it says Available in workers in FF69+ ... can that be used to unmask real measurements? .. also @abrahamjuliot (who is always busy)

@kkapsner
Copy link
Collaborator

DOMRectReadOnly and DOMRect should behave the same (unless an addon messes with one but forgot the other). In fact DOMRect inherits from DOMRectReadOnly:
image

it says Available in workers in FF69+ ... can that be used to unmask real measurements?

I don't think so. There is no document in the worker and no rendering of HTML.

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Nov 29, 2020

Thanks

This is what I thought ... a worker runs in a global context which is different from the current window - so I was surprised to see that listed there and wondered if there was some "workaround" like passing a "context" or "element" reference?

unless an addon messes with one but forgot the other

Yup, that was my point. We test things, like iframe access methods .. and whammo, someone forgot something (I see Brave still hasn't patched those iframes with canvas - etc see https://github.com/brave/brave-browser/issues/12453 ).

So if domrect was altered, the "parent" domrectreadonly might not be? We should make a test for that, right?

@kkapsner
Copy link
Collaborator

We should make a test for that, right?

Should be easy enough.

@Thorin-Oakenpants
Copy link
Contributor Author

@kkapsner It's going to take me a while before I get around to the SVG and other "domrect" stuff, but I had a sneaky thought about css aspect-ratio ...

not sure what it could be used with, and I'm yet to try .. but I'm thinking this could be used to generate boxes with known decimals to detect spoofing - it's on my massive ToDo list, but I just thought I'd throw this idea at you

@kkapsner
Copy link
Collaborator

kkapsner commented Dec 1, 2020

I think it will behave similar to a height or width set with decimals: up to certain precision the browser will match that but will do other numbers beyond it.

@Thorin-Oakenpants
Copy link
Contributor Author

Yeah, it's in my notes: I was thinking I could force something like 1/3 and the result should be 1.3333333333 or whatever, but it may depend on the limitations of the API (not the measuring one) and variables such as pixel snapping, rounding, devicePixelRatio/subpixels, zoom/css-devicePixels (I think I could work around this) .. just something devious I thought about last night as I went to sleep

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Dec 5, 2020

@abrahamjuliot iframe.contentWindow properties change if the console (both web console or browser console) is open: I'm about to head off, so I haven't debugged what changes - the counts stayed the same

edit: diff is
diff

I don't know if you sort yours: I obviously don't: and I wonder if there is a workaround: such as always put Event last: i.e remember it and append it to the array at the end

edit2: 3c34ae7 - but I wonder if anything else can affect the order: I don't like sorting if I can help it, as I want max entropy

@abrahamjuliot
Copy link
Collaborator

I leave it unsorted. Moving Event is nice workaround for the console issue. There might also be a number of prefs that remove/add properties and/or change the order, and sorting might not handle these. I've noticed a change in these modes:

  • RFP
  • TB Safer Security Level
  • Dev tool device simulation (responsive design mode)

@Thorin-Oakenpants
Copy link
Contributor Author

If RFP removes items (which is what I suspect), then sorting isn't going to do jack shit. It's not like prefs change all the time - e.g. RFP changes lots of metrics and is not something that users would generally toggle: it's not available yet per ELTD+1 and users would/should be selective in what they disable it on: and when RFP is off, this might break linkability with previous FP's: but smart backend algorithms can still linkify stuff - and it's also likely to be sites they log into, such as twitter/imgur to upload images, or gmail to get their timestamps in sync with gmail app etc.

But other factors are a concern: I wonder what headless does for example: that would be cool to catch. Needs a bit more investigation - e.g safer vs standard might be some SVG items getting removed - nothing you can do about that. It's really only when the order changes - like consoles .. damnit

Maybe it's just better to sort it: because there are too many unknowns - what do you think?

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Dec 5, 2020

I think sorting is good to handle the console issue. We could capture both and then leave the unsorted in the loose fingerprint.

@Thorin-Oakenpants
Copy link
Contributor Author

items are removed at a minimum: so sorting won't do anything (but would if TB didn't change WASM)

Tor Browser of course
diff-slider

FF+RFP
diff-RFP


order is changed: and it goes a bit deeper

If you ever select the storage tab in web console, Location is also moved to the end (not sure if before or after Event as my code is making Event last) .. and until you close the web console, regardless if the storage tab is selected, Location is always moved. Closing the console clears it.

Not sure where to find responsive design mode.

So depending on what a dev is doing (I'm not sure what these are called: JS actors?) .. some properties get loaded first/last, used last or something

@Thorin-Oakenpants
Copy link
Contributor Author

I think sorting is good to handle the console issue. We could capture both and then leave the unsorted in the loose fingerprint.

There's too many unknowns as to what affects order. I was using this to check for changes between releases (to see if anything popped out for a version check) ... I'll think I'll check diffs per release (constant, no console etc) vanilla profiles to see what diffs there are - maybe sorting doesn't really affect entropy (it probably shouldn't: i.e if wasm affects those other items, but wasm is missing, it's already different - if you get my drift)

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Dec 5, 2020

some tests: windows, practically vanilla profiles (but I do have some tweaks, but they're all the same: mostly UI stuff)

no sorting:
=======
- checked 78->79, 79->80, 80->81
- always something added/removed (ughh I guess not in 80->81: I have a shitty diff utility)
- sorting is about 50 to 70 items in different positions (moved in several clumps)

ESR78.5/FF78
- a80ba2970933d469aae7931685b177ff714d68a4 [813]
FF79
- bfd5ad71cda7986c335f6588dda93b2b0383c30c [816]
FF80
- 1c1e85c5750649a399583735983d544d36611fc2 [816]
FF81
- bf3506af3c49786155c0eb820afe853a968ed17c [816]
FF82
- 533f018257c32c37ac04480ae7e968ac387b94f2 [820]
FF83
- ee628954b9e5b2880bda2b12578f727cccefa6ba [819]
FF84Dev + Beta
- 0070d3816d8503d0068112e5a82e6c03a51369c6 [820]
FF85
- edcb17d31555f464f9fdf4a676cded1748b215b4 [836]

sorted
======
78
- eb06d1c4372efeef24c472f28b152f19323ced86 [813]
79, 80, 81
- 4cf8663aa364f09b2608623274e3f8c08a81c867 [816]
- diffs: 79 added 3 new props
82
- 3d4dbd0937be983cbd6ec05307f12693723fae4d [820]
- diffs: 82 added 4 new props
83
- 2056b9319ff50c03f328dcfab6dac98560401596 [819]
- diffs: 83 removed one of the 4 new props added in 82
84
- 0d2d1b2452e598d1c039f9a79fb3e2641a5e1599 [820]
- diffs: 84 added a new prop
85
- c273969d358ba2f0615e1becabb8fb68417e6379 [836]
- diffs: lots .. it's nightly with loads of testing new shit

So as you can see, the order is different per release (at least on windows, but I don't see why that wouldn't be the same on any platform). But when we sort then there may be no differences: it depends if the stable release gets some new feature - e.g. 79, 80 and 81 were the same for me

That said .. I already have a version number from feature detection - so I'm know I'm good for the overall FP in that regard. I'm just going to sort it and be done - but I do wonder if this cuts possible extra "stable" entropy.

I did notice that the only items ever affected started with a capital (AFAICT, didn't take notes or always check 100%)

@abrahamjuliot
Copy link
Collaborator

responsive design mode

It's the tiny phone/tablet icon in the console menu (or ctrl+shift+M).

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Dec 10, 2020

stuff I need to learn about: https://palant.info/2020/12/10/how-anti-fingerprinting-extensions-tend-to-make-fingerprinting-easier/ - @abrahamjuliot FYI

also: https://news.ycombinator.com/item?id=25374383

gorhill

This bind() call makes sure the getter looks like a native function. Exactly what we needed.

Even the example given as finally working will show difference with the native method, the bound function will have a property name set to bound, while the native one has a name property set to get width.

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Jun 1, 2021

bots: https://www.tomanthony.co.uk/blog/googlebot-javascript-random/

I'm not sure this holds any more, not that I particularly care about bot detection

edit: there's always a xkcd for everything: https://xkcd.com/221/

@Thorin-Oakenpants
Copy link
Contributor Author

good article - https://www.otsukare.info/2022/10/25/css-values-definitions - @abrahamjuliot FYI

@Thorin-Oakenpants
Copy link
Contributor Author

heh

Can you explain why someone would want to scale an image to over 17 million pixels high? On a 96dpi screen, that's over four kilometers high

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

No branches or pull requests

3 participants