Open Bug 1631673 Opened 1 year ago Updated 2 months ago

WhatsApp Web images broken if you flip `privacy.resistFingerprinting` due to canvas prompts without user interaction

Categories

(Core :: DOM: Security, defect, P3)

75 Branch
defect

Tracking

()

UNCONFIRMED

People

(Reporter: jeff.roedel.isp, Unassigned, NeedInfo)

References

(Blocks 1 open bug)

Details

(Whiteboard: [domsecurity-backlog1])

Attachments

(2 files)

User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0

Steps to reproduce:

Go to web.whatsapp.com, log in using QR code. In a chat, attempt to send a picture to someone using the attachment (paper clip) icon, "Photos and Videos", choose a picture file.

Send it.

The bug can be remedied by changing privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts to false. This then will show the user a warning that the Canvas is being blocked.

I've experienced whatsapp web problems in Firefox for years and it's quite frustrating since it's by far the page that I most interact with.

A service with 1.5 billion users should be top priority!

I do appreciate all your work. I fended off the temptation again this week to switch to Chrome. Keep up the good work

Actual results:

The sent photo (also in the preview) appears completely white

Expected results:

The sent photo appears with the photo content of the selected file.

Please note, the User Agent listed in the report is incorrect. I'm using up-to-date stable 75.0 64bit on Windows 10

Not a security bug that needs to stay hidden.

Group: firefox-core-security
Summary: Default setting breaks WhatsApp Web → WhatsApp Web broken if you flip `privacy.resistFingerprinting`

Note that this is only happening because you toggled privacy.resistFingerprinting. It doesn't happen in the default configuration.

Component: Untriaged → Canvas: 2D
Depends on: 1446472, 1376865
Product: Firefox → Core
Summary: WhatsApp Web broken if you flip `privacy.resistFingerprinting` → WhatsApp Web images broken if you flip `privacy.resistFingerprinting` due to canvas prompts without user interaction

It's not just whatsapp: Here's a recent one about instagram [1], and I'm sure I've seen reports of Twitter also breaking. I don't have accounts on either (or whatsapp), so I can't attest to the urlbar just showing a canvas icon, or prompting.

[1] https://old.reddit.com/r/firefox/comments/g1xerq/posts_to_instagram_are_only_white_blank_images/

Can we give implied per-document permission to scraping a canvas to which the site draws an image that comes from a File object? AIUI it can't get a valid file object without the user giving it one, and at that point it should be allowed access to the image data without jumping through more hoops.

Component: Canvas: 2D → DOM: Security
Flags: needinfo?(arthur)
Priority: -- → P3
Whiteboard: [domsecurity-backlog1]

(In reply to :Gijs (he/him) from comment #5)

Can we give implied per-document permission to scraping a canvas to which the site draws an image that comes from a File object? AIUI it can't get a valid file object without the user giving it one, and at that point it should be allowed access to the image data without jumping through more hoops.

I think this is a good idea. I'll cc Matt in; but leave Arthur's ni in case he wants to add an opinion.

Implementation-wise - I'm not sure how to build what you described. How would we know if the image that is drawn came from a File upload? Tracking that data seems infeasible.

However, if the user uploads a file from their computer to a website, I would consider that to be an action that imparts trust in the website; and take that mere action as granting permission to read data from any/all canvases on the page.

I'm no browser export, but what about just disabling the "privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts" functionality after a file upload? The real problem here is that the user isn't notified of the blockage. Once notified, it's trivial to allow the page Canvas access via the Dialog box and re-upload the image.

(In reply to jeff.roedel.isp from comment #7)

I'm no browser export, but what about just disabling the "privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts" functionality after a file upload?

That's equivalent to what's being discussed, but on a per-site basis. The protection the fingerprinting canvas prompt affords shouldn't be turned off on other pages.

The real problem here is that the user isn't notified of the blockage. Once notified, it's trivial to allow the page Canvas access via the Dialog box and re-upload the image.

There should already be an icon appearing in the location bar (to the left of the URL) when this happens. I just tested and this happens for me. You can click to see this doorhanger to allow permissions and then re-upload - it's probably quite subtle though...

(In reply to Tom Ritter [:tjr] (OOTO until 5/1?) from comment #6)

(In reply to :Gijs (he/him) from comment #5)

Can we give implied per-document permission to scraping a canvas to which the site draws an image that comes from a File object? AIUI it can't get a valid file object without the user giving it one, and at that point it should be allowed access to the image data without jumping through more hoops.

I think this is a good idea. I'll cc Matt in; but leave Arthur's ni in case he wants to add an opinion.

Implementation-wise - I'm not sure how to build what you described. How would we know if the image that is drawn came from a File upload? Tracking that data seems infeasible.

Well, we track what we put on the canvas for tainting purposes (ie if cross-origin data is written, the canvas is marked tainted). So presumably when drawImage is called, we know something about the origin of the image. Naively, it would seem possible to pass through where that data came from in the file upload case, too.

However, if the user uploads a file from their computer to a website, I would consider that to be an action that imparts trust in the website; and take that mere action as granting permission to read data from any/all canvases on the page.

This would be simpler and probably work just as well, yes.

(In reply to :Gijs (he/him) from comment #8)

(In reply to jeff.roedel.isp from comment #7)

I'm no browser export, but what about just disabling the "privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts" functionality after a file upload?

That's equivalent to what's being discussed, but on a per-site basis. The protection the fingerprinting canvas prompt affords shouldn't be turned off on other pages.
[...]

However, if the user uploads a file from their computer to a website, I would consider that to be an action that imparts trust in the website; and take that mere action as granting permission to read data from any/all canvases on the page.

This would be simpler and probably work just as well, yes.

Tom, just to make sure that we're on the same page: if the user uploads data to website X and website X requests to extract data from the canvas, then we will prompt the user (as if the user set privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts = false).

I am asking because, I am writing a patch and I wanna make sure that my design makes sense before I get too invested. The above design seems to require a new kind of permission (we seem to currently only have a canvas permission which, when true, allows the website to extract data from the canvas without a prompt.)

Flags: needinfo?(tom)

(In reply to sanketh from comment #9)

Tom, just to make sure that we're on the same page: if the user uploads data to website X and website X requests to extract data from the canvas, then we will prompt the user

No, we should automatically allow the canvas extraction with no prompt. The code will go right before this block. But how to propagate that the user has uploaded a file to this website to this function is a big question mark for me - Gijs will be the reviewer for that :)

Flags: needinfo?(tom)

I don't know what is currently technically feasible, but I have concerns/questions :)

When the user initiates a file upload, the expected prompt is auto-granted (proposed). This auto-grant should only apply to file initiated/source canvas objects <-- this is the key and everything else I mention hereafter becomes irrelevant IMO. Otherwise, can the site now use canvas carte blanche? Is the permission per user-gesture, session only, memory only, or does it become a site permission - just because someone uploaded a file doesn't mean they want to be fingerprinted using a standard/common canvas object: as compared to a unique source (file/their image) see [1] ? Also thinking about Tor Browser which doesn't have site permissions (permissions.memory_only) and is in PB mode = which probably mitigates this somewhat: but we need to think about standard non-PB mode.

[1] emphasis mine

However, if the user uploads a file from their computer to a website, I would consider that to be an action that imparts trust in the website; and take that mere action as granting permission to read data from any/all canvases on the page.

I disagree. If the source is random/unique: then the fingerprint is rendered useless anyway. Site permissions shouldn't be overridden by user-actions, but explicitly controlled via site permissions (options, or urlbar canvas icon). e.g. I know I'm safe from canvas fingerprinting when I upload to WhatApp, but unknown to me, you just changed my defence.

I can already see a way to bypass this (I think). Even if permission was auto-applied per canvas object: that canvas object could still be used any way the site wants, before or after (e.g. applying a standard canvas test => fingerprinted). Right?

(In reply to Tom Ritter [:tjr] (OOTO until 5/1?) from comment #10)

No, we should automatically allow the canvas extraction with no prompt.

Could you elaborate on this a little so I can understand it better. Right now, I am a tad uncomfortable with implicit consent. In the case of WhatsApp/Instagram/Twitter it makes sense, but in general case, I am little worried that this could be abused. Thanks!!

The code will go right before this block. But how to propagate that the user has uploaded a file to this website to this function is a big question mark for me - Gijs will be the reviewer for that :)

I was thinking of using the existing permissionManager->TestPermissionFromPrincipal(principal, PERMISSION_CANVAS_EXTRACT_DATA, &permission); line and patching HTMLInputElement::SubmitNamesValues here to give the current website PERMISSION_CANVAS_EXTRACT_DATA. From your above comment, it seems like you don't want to go down the permissions route, what did you have mind?

Flags: needinfo?(tom)

I meant to cancel Tom's ni, not Arthur's ni. 😬

Flags: needinfo?(tom) → needinfo?(arthur)

Because this bug's Severity has not been changed from the default since it was filed, and it's Priority is P3 (Backlog,) indicating it has been triaged, the bug's Severity is being updated to S3 (normal.)

Severity: normal → S3

Crowdmark appears to have the same issue. You cannot submit an assignment in RFP mode, you get blank pages. I wonder if a resolution to Bug 1450398 would get rid of this issue in practice. A quick-and-dirty solution would be to implement a "canvas auto decline whitelist" pref akin to privacy.resistFingerprinting.exemptedDomains mentioned in Bug 1635603 and calling something like principal->IsURIInPrefList([..]) here. Tom, what do you think?

Flags: needinfo?(tom)

(In reply to sanketh from comment #17)

Crowdmark appears to have the same issue. You cannot submit an assignment in RFP mode, you get blank pages. I wonder if a resolution to Bug 1450398 would get rid of this issue in practice. A quick-and-dirty solution would be to implement a "canvas auto decline whitelist" pref akin to privacy.resistFingerprinting.exemptedDomains mentioned in Bug 1635603 and calling something like principal->IsURIInPrefList([..]) here. Tom, what do you think?

I don't think this is meaningfully different from just granting these domains the permission; I'd rather have the comprehensive fix.

Flags: needinfo?(tom)

(In reply to Simon Mainey from comment #4)

It's not just whatsapp: Here's a recent one about instagram [1], and I'm sure I've seen reports of Twitter also breaking. I don't have accounts on either (or whatsapp), so I can't attest to the urlbar just showing a canvas icon, or prompting.

[1] https://old.reddit.com/r/firefox/comments/g1xerq/posts_to_instagram_are_only_white_blank_images/

A Tor IRC user reported a breakage on Twitter when updating the profile picture. It seems to affect jpg uploads but not png uploads...

Attached image twitter-breakage.png

Breakage is becoming significant. Purely from a compat perspective, I think it's time to explore subtle randomizing per eTLD+1 per session: either as the RFP default or as the option to use when allowing or auto-granting in this fix

You need to log in before you can comment on or make changes to this bug.