Is your feature request related to privacy?
Only privacy-related feature requests are considered.
Yes
Is there a patch available for this feature somewhere?
Not that I'm aware of.
Describe the solution you'd like
A clear and concise description of what you want to happen.
Do you know if it's practical to implement a WebGL fingerprinting deception mechanism similar to the one for Canvas image data?
Specifically, for WebGL image hashes as implemented in https://browserleaks.com/webgl
Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
N/A
@Eloston I have thought of this in the past, but I quickly dismissed the possibility because of the possible vectorial nature of WebGL renderings. You can have pretty advanced color shifts (and certainly not limited to what we see in that image hash there) and texture usage, so a single pixel change might cause major visual glitches.
However this is just speculation, I have not actually tried and right now by looking at the approach used there perhaps it is possible. If we look at https://browserleaks.com/js/webgl.js towards the end there is:
try{
var j=new Uint8Array(131072);
if (a.readPixels(0,0,256,128,a.RGBA,a.UNSIGNED_BYTE,j),i=JSON.stringify(j).replace(/,?"[0-9]+":/g,""),""==i.replace(/^{[0]+}$/g,""))
And a is created as:
a=b.getContext("webgl2",{preserveDrawingBuffer:!0})||
b.getContext("experimental-webgl2",{preserveDrawingBuffer:!0})||
b.getContext("webgl",{preserveDrawingBuffer:!0})||
b.getContext("experimental-webgl",{preserveDrawingBuffer:!0})||
b.getContext("moz-webgl",{preserveDrawingBuffer:!0})}
So if we get a hold of readPixels on all of the above (those available in Chromium), then yes we can just re-use the same subchannel color data randomisation approach on the extracted raster. The only boring/lengthy part would be to implement it for all of the modes supported (not just RGBA) as I did for the SkImage case; likely it is using a SkImage internally as well, so the code might be reused.
I will give a look to this and report back here, thanks for opening this.
@Eloston yesterday I did some digging. The readPixels function is defined in https://github.com/chromium/chromium/blob/ffd9d67094adb539dd2cfd34d3a8aaa57c77fcc7/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h but ultimately is implemented by the GL context.
An example of such context (not sure if there are others in the codebase) is the GLES2Interface, as you can see being used in this test here: https://github.com/chromium/chromium/blob/ffd9d67094adb539dd2cfd34d3a8aaa57c77fcc7/cc/paint/oop_pixeltest.cc#L149
The actual ReadPixels is stubbed here though: https://github.com/chromium/chromium/blob/0c92aa0f72e2dd58e8e8a86e053e79f801dce629/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h#L453
Open questions:
Interesting, thanks for the preliminary research.
Here are my thoughts based on what you found:
can we intercept the result?
Based on the documentation on WebGLRenderingContext.readPixels(), note that the data has to be read into a new array; i.e. the data is copied into the JavaScript environment in order to be accessed.
I think we can look into intercepting the JavaScript interface definition for readPixels; from there we can determine how we can manipulate the data being retrieved (e.g. during the copying into the JavaScript array, or after it has been copied).
is Skia really involved?
Skia is purely a 2D graphics library, and this readPixels method seems to be a thin layer around OpenGL calls. I think it's safe to say this isn't the case.
I think we can look into intercepting the JavaScript interface definition for readPixels; from there we can determine how we can manipulate the data being retrieved (e.g. during the copying into the JavaScript array, or after it has been copied).
Yes we could change the data after it has been copied there, but there must be guarantees that access to the ArrayBufferView is exclusively locked at that time.
Skia is purely a 2D graphics library, and this readPixels method seems to be a thin layer around OpenGL calls. I think it's safe to say this isn't the case.
It would be a bit strange to me if they're converting to Skia first, since readPixels seems like a thin layer around the GL calls as I mentioned earlier.
Nevertheless, I would need to start looking at code too in order to contribute something meaningful to this discussion. I can't guarantee I will have the chance to do so anytime soon, though.
Based on the documentation on WebGLRenderingContext.readPixels(), note that the data has to be read into a new array; i.e. the data is copied into the JavaScript environment in order to be accessed.
I think we can look into intercepting the JavaScript interface definition for
readPixels;
@Eloston the corresponding IDL functions should be here: https://github.com/chromium/chromium/blob/bf31bedf12761be9d4e56500559f2777cbcbeedd/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc#L734
Interception should be possible right after the call to ReadPixelsHelper, or better inside that function because we do not want to edit pixels if no pixel data was added; specifically here: https://github.com/chromium/chromium/blob/53f3d30ef104e4c91ab0d7eb9683305139072ef3/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc#L4345
If you check that file you will find several calls like ContextGL()->ReadPixels(), that ReadPixels with uppercase R comes from the context, like in the case of GLES2 here:
https://github.com/chromium/chromium/blob/2ca8c5037021c9d2ecc00b787d58a31ed8fc8bcb/gpu/command_buffer/client/gles2_implementation.cc#L3808
Next step would be to check which pixels format are maximally supported, so that we know which ones to implement.
Also check the font fingerprint
https://browserleaks.com/fonts
Related: https://bugzilla.mozilla.org/show_bug.cgi?id=1428034
Edit: I have added the WebGL image hash to the fingerprinting mitigations page: https://www.bromite.org/detect
I have found that even if both WebGL and WebGL2 use ReadPixelsHelper, the poorly documented WebGL2ComputeRenderingContext does not use that function when providing readPixels.
I am saying "poorly documented" because I could not find a W3C or other draft describing it, only this document from Yunchao He (@ Intel): https://docs.google.com/document/d/1EwhDJO_lBH1mGMMwheQUXGhhFk9yoC98Ant3TPqwmmA/view
You can see that readPixels is exposed in the IDL (although not documented in the previous document):
https://github.com/chromium/chromium/blob/cf7ed613af7b01f2e64929f969d3737067e28083/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt#L8610
Covering this method as well poses new challenges as it uses an internal buffer; it might be simpler to remove this undocumented feature of a non-standard interface.
I would consider adding an option for turning off WebGL entirely. As much for it having no real use aside for obscene 3D popups, as for near impossibility of sanitising the shader code.
Somewhere in 7x.x development, Google disabled the flag to disable the WebGL, so now I believe we have to put it back.
I would consider adding an option for turning off WebGL entirely. As much for it having no real use aside for obscene 3D popups, as for near impossibility of sanitising the shader code.
Somewhere in 7x.x development, Google disabled the flag to disable the WebGL, so now I believe we have to put it back.
However, this could provoke a raise in the uniqueness of your fingerprint as a "not supported/broken webgl" support.
Flags for turning off WebGL have been added, see #411; they are not disabled by default in Bromite.
this could provoke a raise in the uniqueness of your fingerprint as a "not supported/broken webgl" support.
@PoorPocketsMcNewHold by disabling WebGL/WebGL2 you are swapping n bits (whichever amount of entropy they convey) with 1 bit, effectively reducing the leak. This bit will however be used in combination with all the other sources, so that in the best case users can be identified as Bromite users and in the worst case as detailed as any Chromium browser allows (even individually).
Some things I noticed:
Some questions:
Some questions:
- Any chance for Bromite for Windows? It would be a heavy competitor for Brave.
Go for ungoogled-chromium.
Bromite take some patches from it.
- Why disable QUIC by default? Does it pose a security and/or fingerprint risk?
https://brave.com/quic-in-the-wild
https://bugs.chromium.org/p/chromium/issues/detail?id=715140#c11
https://bugzilla.mozilla.org/show_bug.cgi?id=1158011
https://github.com/diracdeltas/quic-request
Thanks!
FYI, on original rooted Pixel + LineageOS 17.1 Nightly (Android 10) + Magisk + Magisk Bromite Module, Bromite WebView works perfectly, does not break anything, can be selected in Developer Options. Android WebView can be removed fully. Its awesome!
Flags for turning off WebGL have been added, see #411; they are not disabled by default in Bromite.
this could provoke a raise in the uniqueness of your fingerprint as a "not supported/broken webgl" support.
@PoorPocketsMcNewHold by disabling WebGL/WebGL2 you are swapping _n_ bits (whichever amount of entropy they convey) with 1 bit, effectively reducing the leak. This bit will however be used in combination with all the other sources, so that in the best case users can be identified as Bromite users and in the worst case as detailed as any Chromium browser allows (even individually).
Hello, i don't find WebGL OFF flag in Bromite 85.0.4183.86
Can you please indicate the exact path of how to find it?
Most helpful comment
Related: https://bugzilla.mozilla.org/show_bug.cgi?id=1428034
Edit: I have added the WebGL image hash to the fingerprinting mitigations page: https://www.bromite.org/detect