Closed Bug 1375683 Opened 7 years ago Closed 9 months ago

[meta] Consider mitigations for push notification abuse by websites

Categories

(Core :: DOM: Push Subscriptions, enhancement, P2)

53 Branch
enhancement

Tracking

()

RESOLVED INACTIVE

People

(Reporter: bzbarsky, Unassigned)

References

(Depends on 3 open bugs)

Details

(Keywords: meta)

We implemented push notifications permissions with a popup that requires the user to make a choice; you can't just click somewhere random to make the whole thing go away.

Unfortunately, various sites are abusing push notifications and prompting as soon as you load the page.  Since the user _has_ to respond to the prompt, this is a pretty terrible user experience.

See https://github.com/WICG/interventions/issues/49 for some discussion about the problem and some data on what Chrome is considering doing.

For our part, we should at the very least make it possible to dismiss without choosing, and probably implement other mitigations too.

Not sure whom to cc about the UX impact here...
Flags: needinfo?(overholt)
Panos, who can help with the UX here? Why did we make it impossible to dismiss the prompt without choosing?
Flags: needinfo?(past)
Bryan has thought about plans here and I think Kit has, too.
Flags: needinfo?(overholt)
Flags: needinfo?(kit)
Flags: needinfo?(clarkbw)
Priority: -- → P2
I believe Bryan has all the context, but adding Peter for the privacy and security angle.

I have experienced this behavior, but I wouldn't characterize it as terrible. The Don't Allow button is a huge click target. What I find depressing however is that some of the partners that helped us design this interaction haven't followed through with implementing it and serving as a positive example. The privacy team has been evangelizing the right way to implement push notifications (my talk was a few weeks ago), but I absolutely agree that we should experiment with other proposed countermeasures like requiring a user gesture.
Flags: needinfo?(past) → needinfo?(pdolanjski)
FWIW I've seen a number of users request the ability to disable notifications globally.  See bug 1313939.  Unfortunately I have not had any luck in getting UX to take action on this user feedback.
See Also: → 1313939
Pre-53, you could dismiss the doorhanger without making a decision by clicking outside it. For other permissions, we minimized the doorhanger into the identity block, so that you could restore and grant permission later; for notifications, we returned `"default"` to the site. As Panos says, the new modal doorhanger was repeatedly requested by partners; it is a bit unfortunate that they haven't followed through with adopting best practices.

I agree with Ben that disabling notifications globally would help (more discussion in bug 1275599, bug 1363856, bug 1364942). Thanks for linking to https://github.com/WICG/interventions/issues/49, too.
Flags: needinfo?(kit)
See Also: → 1368744
We should get some UX in here to figure this out.  I can see how a global option would be nice but it also breaks the experience in ways people might not notice. 

Perhaps we can focus on ways to prevent the permissions from being annoying in certain scenarios instead of trying to block requests.
Flags: needinfo?(clarkbw)
(In reply to Bryan Clark (DevTools PM) [:clarkbw] from comment #6)
> We should get some UX in here to figure this out.

Do you know if this is underway?
Flags: needinfo?(clarkbw)
So, two things that _are_ underway that offer some possibilities to tackle the notification spam problem:

- I've started adding a user gesture flag to nsIPermissionPrompt in bug 1345424. We could use that to enforce a user gesture before prompting (although that's of course a big web compat issue). I didn't really finish it because I was resourced to Photon but I might pick it up again, since this issue is becoming more and more pressing. I don't think there's a large amount of work left.

- I'm implementing a way to have a clean global disable of permissions in bug 1379560. The big advantage is that that's not a DOM pref, it's just a default state for the respective permission, so users can globally hide permission prompts but whitelist specific sites they like (e.g. WhatsApp, Facebook).

These are both platform fixes, we'd indeed need some Product/UX help to properly integrate them without breaking the web for everyone. One thing that has at least UX commitment (but no spec yet) is adding a global disable toggle (presumably with the ability to whitelist) in bug 1368744.

I'm marking those bugs as blocking, to be able to track the work we're doing and not duplicate efforts.
Depends on: 1379560, 1345424, 1368744
See Also: 1368744
I think @phlsa is our UX person here or can direct us to the right person.

Question is: What do we do about sites that annoy people with their immediate request for push permission?

Tom's is a good example of this: http://www.tomshardware.com/

There are suggestions to be able to block all requests for all sites.  I'd push against this because I see it as the "Turn off JavaScript" type of option that avoids issues but also breaks other sites in unforeseen ways.

Here's a couple user stories I think we want to consider:

As a user who navigates to a site for the 1st time I don't want to be bombarded with permission requests such that I have to say no when I'm just investigating this site.

As the developer of a game site I want to be able to ask for user permissions quickly such that I can immerse people into the experience immediately.

It might also be worth considering that, at least for now, I don't believe we've seen sites where the Push permission is required immediately.  So on first visit or in the first several seconds game sites might want other permissions but Push is more about re-acquisition of users and not keeping them on the site.
Flags: needinfo?(clarkbw) → needinfo?(philipp)
One use case I have to add to comment 9:

When setting up a new profile I add the telegram messaging site as a pinned tab.  I would like it to be able to ask for notification permissions immediately.
Depends on: 1345077
I think that in order to provide a good user experience, we should really take a step back and consider whether incremental fixes are the right way to go, or whether we need to rethink the whole approach of showing notifications from the ground up and build it around the idea of not requiring permission prompts in the first place.  I would like to argue that any of the solutions of the first category will fall short of the ideal user experience (which is, to allow sites to send notifications for useful things in a way that doesn't annoy users), and we should take the second path.

First, here are some of the problems with ideas along the first approach.  What I would like to emphasize here is that what matters above all else is the *default experience* that users get out of the box.  We know from a variety of research that users are averse to changing settings in their software.  So approaches like bug 1379560 aren't a sufficient fix here because they only address the problem for the tiny minority of users who go out of their way to discover that setting and turn it on, and even worse that precludes all sites from that point on to send notifications unless if the user configures their browser even further.  Also, ideas such as bug 1345424 and https://github.com/WICG/interventions/issues/49#issuecomment-310445655 only improve things a tiny bit, since there is really not much reason to believe that sites that the user interacts with (a bit, or to some extent) are exactly the sites that the user is OK to receive notifications from.  Using interaction/engagement as a proxy here seems quite weird.

But if we were to design things differently from scratch, here is one possible approach that wouldn't require permission prompts at all.  Let's imagine that the browser had a Notifications Sidebar.  By default, all sites would have a free pass to send you notifications but instead of the notification going to the desktop (or to the phone notification UI) it would go to the browser's Notifications Sidebar.  In terms of the visible UI changes when that happens, we'd need something really subtle, like an animated icon that would tell the user something happened, but not more than that.  Once the user clicked on that, they could see the recent notification plus all of the ones sent in the past.  There, we would also show a button to the user like "Send This Site's Notifications To Desktop", which would have the same impact as saying Allow to our current Notification Permission Prompts.

The effect of changing our UI this way would be that we would remove the step where we have to ask the user a question, by redoing things in a way that doesn't require asking a question in the first place (since once you have a place to put notifications in all the time that isn't in the user's face, then you don't need to ask them permission to display those notifications...)  The details of the idea I'm proposing could be adjusted of course but the crux of the idea is to remove the question we're asking this permission around.
Push notifications imply the ability to wake up a ServiceWorker when the user isn't browsing the site.  So the permission grant is doing (secret) double-duty, hopefully setting the expectation that the website will be able to do things in the background.  This, of course, is not necessarily entirely obvious, so some browsers (like Chrome) will display a notification if a SW that received a "push" event fails to display its own notification (and in a timely fashion).  Based on brief research, I think the notification will say "This site has been updated in the background." with the origin labeled[1].

Which is to say, I don't think granting the ability to dispatch "push" notifications but hiding the notifications is a workable strategy based on the current execution model.  (One could of course create an exciting new sub-genre of anti-virus software to rein in the worst excesses...)

Looking into the abandonment of the Budget API[2], :mt's post at https://discourse.wicg.io/t/proposal-budget-api/1717/7 suggests he might be an excellent person to consult with in this space.

1: I'm going off of the screenshot at https://developers.google.com/web/fundamentals/push-notifications/handling-messages
2: https://github.com/WICG/budget-api/issues/23
I like the way that Ehsan is thinking here.

One thing that came up in discussions is the removal of the ability to summon the doorhanger unless the request was a direct result of an engagement gesture.  Sites that call the geolocation API at page load time would then cause an icon to appear in the control centre only.  Activating the control centre would either cause the doorhanger to display or maybe just highlight the permissions that have been requested.

The key here is that this requires explicit action to be elevated into an annoyance.  We would treat display of the doorhanger as morally equivalent to a popup, which I think is fair, based on how badly they are being abused.

Changes like this will guarantee complaints from some sites.  I don't think that it's a big deal, but we should be prepared to answer those complaints.

(In reply to Andrew Sutherland [:asuth] from comment #13)

Push notifications imply the ability to wake up a ServiceWorker when the
user isn't browsing the site. So the permission grant is doing (secret)
double-duty, hopefully setting the expectation that the website will be able
to do things in the background. This, of course, is not necessarily
entirely obvious, so some browsers (like Chrome) will display a notification
if a SW that received a "push" event fails to display its own notification
(and in a timely fashion). Based on brief research, I think the
notification will say "This site has been updated in the background." with
the origin labeled[1].

Which is to say, I don't think granting the ability to dispatch "push"
notifications but hiding the notifications is a workable strategy based on
the current execution model. (One could of course create an exciting new
sub-genre of anti-virus software to rein in the worst excesses...)

Fair criticism. I will say though that the "This site has been updated in the background" prompt is a terrible solution to the problem as well, since I posit that users generally do not have a mental model suggesting that sites are things that can run in the background, based on how sites have always worked. (I am willing to relinquish my position if we ever find user research that shows that users think of sites the same way as they think of "applications" running on their devices.)

I still think there are ways to mitigate the abuse scenario with the model that I suggested. I spent some time thinking about how we would do that. I took inspiration from a "background app optimizer" feature that I have in my HTC Android customization on my phone (I wish I could tell you where the software actually comes from... :/ )

The way that it works is that it allows applications to send push notifications freely, and it monitors how frequently you interact with them. If it notices you never interact with an app's notifications for three days or longer, it automatically adds the application to a block list of "optimized" applications which aren't allowed to receive push notifications while in background. When you open the app again, it is removed from that list. This works extremely well in removing the annoying notifications from apps that I never want to use (e.g. things I opened once to deal with a ticket, etc.)

Borrowing from the same concept, Firefox could monitor which sites send you invisible push notifications while in the background and keep tabs on whether you end up using the sites or not for a defined period of time (e.g. 3 days). When it notices you haven't used such a chatty site for 3 days or longer, it can add the site to the list of "optimized sites to not run in background in order to save battery life" or some such which would be visible and possible to manage in the Notifications Sidebar and block further push notifications from being delivered to that site. We can also have other heuristics for highly abusive scenarios (e.g. a maximum invisible push notification quota per day, per hour and per minute for each website with similar treatments once they go over quota).

Of course, the unfortunate side of this model would be that we would still tell the user about sites running in the background, but at least not for every single push notification, so if a site only sends one or two of these notifications the user wouldn't be bothered with any such prompts. The good side of the prompts over what Chrome shows is that the prompts we would show are something that tells the user something useful (we're saving you battery life, and preventing an abusive website) rather than an informational non-sequitur the user can't do anything about (hey this thing just happened, and yeah that's it, carry on with your life).

Obviously a person with actual UX skills could put me to real shame in designing something that is actually decent here, I'm just trying to expand the Overton window in our discussion around this UI, not by any means suggesting what actual UI we should have here. :-)

I'm still concerned about the prospect of providing free delayed execution. But I think we could probably leverage your notification sidebar idea to deal with this.

The pipeline of states leading to a displayed desktop notification is:

  1. Site requests push notification permission.
  2. The permission is granted.
  3. Site sends push notification.
  4. ServiceWorker gets to run.
  5. Desktop notification may possibly be displayed (somewhere)

Right now, we stall things and require annoying user interaction at step 1. Your proposal is to let sites get to 5 but eventually we shut them down after we've detected abuse, lack of user interest, or explicit user dis-interest.

We could potentially let all sites get to step 3 for the first time, but not actually run the ServiceWorker (step 4) until the user has expressed positive interest by tapping on a notification thingy that says "example.com wants to send you a notification". Once they've expressed interest, we let the ServiceWorker run and see how things shake out, with a desktop notification potentially being displayed.

The obvious downside to this strategy is it's possible that:
a. The SW may take some time to run leading to latency while the user has to stare at it waiting for a notification to show up.
b. The site may no longer want to show a desktop notification and so there's nothing to show. For example, a calendar notification for an event now in the past or an email that's already been read, etc.
c. The site is a nefarious bad site and the user is now watching as nothing happens and the site just burns CPU mining bitcoin.
d. The user may be offline right now...

For 'a', well, we want the push notification handling to be fast and responsive, so if the user ends up deciding to penalize the site for having a slow/laggy SW, that's okay. For 'b', well, that's less good, but we can at least have granted the permission from user intent. Sites could also learn to show a desktop notification in this case. Step 'c' is a case where it could be good for the user to explicitly blocklist the site, and we could help this by informing them that the site ended up using CPU/battery for N minutes, but it's still weird UX. For 'd', I think the notification bar would need to display "you're offline, but see these next time?"

It would also be possible to put something in the notification bar once the permission had been requested and auto-granted (pipeline step 1). And it would also be possible to speculatively grant full permissions to run SW based on an overwhelming indication of use of the site per history/heuristics.

(In reply to :Ehsan Akhgari from comment #15)

Borrowing from the same concept, Firefox could monitor which sites send you invisible push notifications while in the background and keep tabs on whether you end up using the sites or not for a defined period of time (e.g. 3 days). When it notices you haven't used such a chatty site for 3 days or longer, it can add the site to the list of "optimized sites to not run in background in order to save battery life" or some such which would be visible and possible to manage in the Notifications Sidebar and block further push notifications from being delivered to that site.

Interestingly, I think this is similar to, but not quite the same as, the quota system we have today. Each origin has a limited number of background pushes, capped at 16, and based on the visit time...the more recent your last visit, the higher the quota. For each incoming push, we start a 3 second timer before notifying the service worker. When the timer fires, we check if there's at least one notification visible for the origin, and dock it if not. Once an origin burns through its quota, we "expire" the subscription, where we drop it on the server, but prevent the page from resubscribing (and don't fire the pushsubscriptionchange event) until after the next time you visit the page.

It's fairly opaque, though, and neither users nor developers have much insight into it (by design). The only indication that you sent too many background pushes is...you don't get pushes anymore. It also penalizes sites that want to do the right thing, but take more than 3 seconds to handle the push event: what if they're on mobile and want to fetch something before showing the notification? Conversely, since that quota is specific to push, we'll still wake up the worker for fetch events, even if the site is being nefarious.

(In reply to Andrew Sutherland [:asuth] from comment #16)

I'm still concerned about the prospect of providing free delayed execution. But I think we could probably leverage your notification sidebar idea to deal with this.

The pipeline of states leading to a displayed desktop notification is:

  1. Site requests push notification permission.
  2. The permission is granted.
  3. Site sends push notification.
  4. ServiceWorker gets to run.
  5. Desktop notification may possibly be displayed (somewhere)

Right now, we stall things and require annoying user interaction at step 1. Your proposal is to let sites get to 5 but eventually we shut them down after we've detected abuse, lack of user interest, or explicit user dis-interest.

Yes, this is a good way to think about it. I think the key point that I would like to focus on is to challenge ourselves to try to imagine what it would take for us to completely remove the user from the position of being burdened with deciding which website is allowed or isn't allowed to show notifications, and instead do our best to make push notifications be good citizens in most cases and empower the user to deal effectively with the egregiously abusive ones.

We could potentially let all sites get to step 3 for the first time, but not actually run the ServiceWorker (step 4) until the user has expressed positive interest by tapping on a notification thingy that says "example.com wants to send you a notification". Once they've expressed interest, we let the ServiceWorker run and see how things shake out, with a desktop notification potentially being displayed.

The obvious downside to this strategy is it's possible that:
a. The SW may take some time to run leading to latency while the user has to stare at it waiting for a notification to show up.
b. The site may no longer want to show a desktop notification and so there's nothing to show. For example, a calendar notification for an event now in the past or an email that's already been read, etc.
c. The site is a nefarious bad site and the user is now watching as nothing happens and the site just burns CPU mining bitcoin.
d. The user may be offline right now...

There is another bad side effect too, which is, it may be too late to run the SW code itself, no? For example, for a chat application which writes your chat history into an IndexedDB in sequence, if we hold the SW from executing, and the user opens the site again later before its SW has had a chance to run in the background, the site JS code may end up writing things into the DB before the JS code from the push notification has had a chance to run.

(Perhaps this is a possibility that developers have to deal with anyway due to running out of browser imposed quotas today anyway, I'm not too sure.)

For 'a', well, we want the push notification handling to be fast and responsive, so if the user ends up deciding to penalize the site for having a slow/laggy SW, that's okay. For 'b', well, that's less good, but we can at least have granted the permission from user intent. Sites could also learn to show a desktop notification in this case. Step 'c' is a case where it could be good for the user to explicitly blocklist the site, and we could help this by informing them that the site ended up using CPU/battery for N minutes, but it's still weird UX. For 'd', I think the notification bar would need to display "you're offline, but see these next time?"

It would also be possible to put something in the notification bar once the permission had been requested and auto-granted (pipeline step 1). And it would also be possible to speculatively grant full permissions to run SW based on an overwhelming indication of use of the site per history/heuristics.

Yes indeed these are all options we could explore, depending on how much of a problem we consider running the SW code before prompting the user to be.

(In reply to Lina Cambridge (she/her) [:lina] from comment #17)

(In reply to :Ehsan Akhgari from comment #15)

Borrowing from the same concept, Firefox could monitor which sites send you invisible push notifications while in the background and keep tabs on whether you end up using the sites or not for a defined period of time (e.g. 3 days). When it notices you haven't used such a chatty site for 3 days or longer, it can add the site to the list of "optimized sites to not run in background in order to save battery life" or some such which would be visible and possible to manage in the Notifications Sidebar and block further push notifications from being delivered to that site.

Interestingly, I think this is similar to, but not quite the same as, the quota system we have today. Each origin has a limited number of background pushes, capped at 16, and based on the visit time...the more recent your last visit, the higher the quota. For each incoming push, we start a 3 second timer before notifying the service worker. When the timer fires, we check if there's at least one notification visible for the origin, and dock it if not. Once an origin burns through its quota, we "expire" the subscription, where we drop it on the server, but prevent the page from resubscribing (and don't fire the pushsubscriptionchange event) until after the next time you visit the page.

It's fairly opaque, though, and neither users nor developers have much insight into it (by design). The only indication that you sent too many background pushes is...you don't get pushes anymore. It also penalizes sites that want to do the right thing, but take more than 3 seconds to handle the push event: what if they're on mobile and want to fetch something before showing the notification? Conversely, since that quota is specific to push, we'll still wake up the worker for fetch events, even if the site is being nefarious.

Thanks, that is great to know. The fact that SWs have to deal with this scenario in their push handlers is good news to me since it may mean that if a browser decides to explore an alternative avenue based on some of these ideas in order to remove permission prompts for push notifications then maybe some web compat pain could be avoided.

Depends on: 1524619

Note that bug 1429016 is on file remove or extend the expiration on some notification telemetry (related to the prompt, saved permissions, and the notifications themselves). If you think these would be useful to extend, please let me know this week as a contributor attached a patch to remove them all.

See Also: → 1429016
Depends on: 1508961

We are working on this.

Depends on: 1534456
Flags: needinfo?(philipp)
Flags: needinfo?(pdolanjski)
Keywords: meta
Summary: Consider mitigations for push notification abuse by websites → [meta] Consider mitigations for push notification abuse by websites
Depends on: 1536454
Depends on: 1536413
Depends on: 1540088
Depends on: 1544424
Depends on: 1549761
Depends on: 1570674
Depends on: 1572694
Depends on: 1560741
Depends on: 1647639
Severity: normal → S3

We shipped the mitigation in bug 1593644 in Fx72, right? For now I'll mark this as inactive (and not fixed as there are still some subitems).

Status: NEW → RESOLVED
Closed: 9 months ago
Depends on: 1593644
Resolution: --- → INACTIVE
You need to log in before you can comment on or make changes to this bug.