FCM Security: Prevent multiple senders from pushing notifications to all devices? - firebase

As part of our solution, we want to deploy an FCM "app server" at each of our customer sites. Each customer site has their own users with their own devices using our app. However, we want to make sure that if one of the customer sites is compromised, an attacker could not abuse the FCM "app server" (e.g. by sending notifications to all devices at all customer sites).
Instead of sharing credentials between all customer sites, we are thinking of generating a unique server key for each customer site. That way if one customer site is compromised, we can disable that server key and stop any more FCM notifications from being sent.
Question: Can we be sure that an attacker cannot send global notifications to all devices?
Assuming an attacker has a server key and access to one customer site "app-server", can they get a list of all the registered devices?
Is there a default notification "topic" that is sent to all devices? (e.g. /topic/all or /topic/global). If so, can we disable that default topic?

Instead of sharing credentials between all customer sites, we are thinking of generating a unique server key for each customer site. That way if one customer site is compromised, we can disable that server key and stop any more FCM notifications from being sent.
If by "we are thinking of generating a unique server key for each customer site" you mean that you'll simply create a Firebase Project for each customer site, then I think this is the correct approach.
Can we be sure that an attacker cannot send global notifications to all devices?
An app can receive messages from a different Sender by implementing the getToken(authorizedEntity, scope) which will generate a different token for each Sender. In order to negate this action, you could simply call deleteToken(authorizedEntity, scope) (my reference).
This would invalidate the token for that corresponding sender (which is what they probably have and should be the only one on their App Server), which would automatically disable them for receiving messages to your App.
So as long as you're able to remove them as a valid sender from your app, then it's all good.
Assuming an attacker has a server key and access to one customer site "app-server", can they get a list of all the registered devices?
This depends on how the App Server is implemented. If the customer's App server is only used for sending messages, but the tokens are stored elsewhere, then probably no. There is no API to retrieve registration tokens on the server side for an App based on the Server Key (see #1 here).
Is there a default notification "topic" that sends to all devices? (e.g. /topic/all or /topic/global). If so, can we disable that default topic?
There isn't. There is the option to send a Notification to a specific app via the Firebase Notifications Console, but if the app doesn't authorize the Sender ID corresponding to that project, it won't receive any messages from it. I've tested this behavior out before posting, so I'm positive that this is how it works.

There is no way to restrict a server key to only allow certain topics/devices/etc.
I would consider using Cloud Functions for Firebase to solve this a different way. You could build an HTTPS function that took per-site authorization tokens (by any means you deem fit) and then that function calls through to Firebase Cloud Messaging to actually send the push notifications.
This way, you have complete control over what kinds of push notifications can be sent by the "client" sites, and you don't have to worry about cascading security problems in the event a client site gets compromised.

Related

Firebase Admin SDK security best practice for push-notifications

I want to let my customers send push notifications to their users. I am using Firebase Admin SDK for that, which requires the "service-account.json" file. For security reasons this file should not be shared with others. How could I make this feature available to my customers, without sharing any "secret" information?
If you want to allow your users to send push notifications, you'll have to make a custom API endpoint that you can call from the application they use. It's quite common to use Cloud Functions or Cloud Run for this, but any trusted environment you may already have can work too.
By running the code that sends the message in an environment only you can access, allows you to securely use the Admin SDK that has full administrative access to your Firebase project.
Then when this code gets a request from a user running your app, it needs to check whether this user is authorized to send this message to the user(s) they are trying to send this to. Exactly how to check authorization depends on your app, but some things to consider:
Is any user allowed to send a message to any other user, or is there some mechanism where they opt-in to receiving messages from each other?
Can any message be sent, or is there some mechanism that validates the messages, for example by detecting whether any foul language is used?
These are just some examples of the types of checks you might want to do, and the actual list completely depends on your app and its use-cases.

When and what Old FCM Tokens do I need to delete from my backend?

The way I understand Firebase Cloud Messaging at the moment, I will want to save all FCM tokens a user might have across devices to my backend and send a notification to all of these tokens at once when I want to notify that user about something.
Multiple tokens
Until this point, I assumed that each device only has one active token, however, reading this section of the documentation:
To enable this feature, make sure you have each sender's sender ID. When requesting registration, the client app fetches the token multiple times, each time with a different sender ID in audience field, using the token retrieval method for the given platform
Does this mean that I might need to target multiple active tokens per device?
Deletion
And now coming to the really important question. A simple solution to the above problem would be to simply store every token I ever retrieve in the backend and thus ensuring that my user will always receive the message.
However, what do I do if the user signs out of my app using Firebase Authentication, i.e. a different account is used in the same app on the same device?
I assume that the tokens I sent to my backend for this device will still be active - so now this user will receive notifications from another account because that account was signed in on the device previously.
I do have access to the current FCM token and I could delete that from my backend before signing out the old user, but considering the "Multiple tokens" section: how do I make sure that I can delete all FCM tokens of the old user from my backend?
Uniqueness
Additionally, assuming that old tokens are just dead for the device (will not trigger notifications anymore) when new ones are generated, can I be sure that this token will never be assigned to another device in the future?
TL;DR
How can I make sure that I have the correct FCM token(s) for my user stored in my backend and more importantly: how can I ensure that no tokens of other users are saved for some user in my backend?
I read through:
https://stackoverflow.com/a/40158260/6509751
However, I still do not know how to deal with multiple tokens.
Does this mean that I might need to target multiple active tokens per device?
An application has a single active token for each sender ID. It's fairly uncommon to have multiple sender ID, and you'd usually know if if you do. If you're sending from a single back-end, there's usually no need for having multiple sender IDs.

Web Push. fetching web pushes sent while offline?

I am coding a vue.js app using web pushes with Firebase Cloud Messaging and I wondered if it was possible to send a web push to a user and in case the user was offline, to somehow store it for later display when the user opens the app again. Is there a principled approach to this problem, i.e. managing web pushes when the end user is offline?
The reason I am asking this is that, so far, all the web push notifications I've committed to FCM server with tokens of offline clients (i.e. desktop browsers) went into oblivion. To be sure, FCM didn't try pushing the notification again when the clients went back online.
For this reason I am considering coding a self-made dispatcher to manage web push for offline clients, but I need to make sure my efforts are worth it.
updated: I am now able to display notifications sent to an offline client after coming back online using appropriate time_to_live values. However, only the latest notification is displayed. How is there any specific reason why?
FCM's default behavior is exactly like that. From the docs:
If the device is not connected to FCM, the message is stored until a connection is established (again respecting the collapse key rules). When a connection is established, FCM delivers all pending messages to the device.

Can a user's Firebase device ID key be used by multiple Firebase service providers?

If I share a user's Firebase device ID key (for a user who has my app installed) with other Firebase service providers, can they send messages from their account (using their authentication key) to a user who has my app installed?
Yes I do realize the process of sharing a user's Firebase device ID key could be problematic. The problem I am trying to solve is that I want multiple providers to be able to send messages to a user who has my app installed.
The Firebase Instance ID (also known as a registration token, or FCM token) identifies an installation of your app on a specific device.
Sending messages to such tokens in a project always requires an additional form "authentication.
The Firebase Cloud Messaging versioned REST API requires that the user has a service account. If you create a service account for each of your service providers, you grant them complete access to your Firebase project. So they can't only send FCM messages, they can also access every other Firebase product: e.g. delete your database, read all your users, etc.
The legacy REST API for Firebase Cloud Messaging instead uses a Server Key to authorize its callers. If you share your FCM server key with other service providers, they can only send FCM messages with that key. But they can send whatever messages they want to whatever user.
You might want to consider setting up your own API endpoint on Cloud Functions for Firebase. That way you can determine yourself how to secure that API, and what you allow your service providers to send to what users of your app.
Assuming that the Firebase Device ID Key you're referring to is the FCM Registration token, then having the value alone won't enable others to send a message to it without the corresponding Server Key it is associated with.
For your use-case of allowing multiple senders to a single app, you could refer to the official documentation on Receiving messages from multiple senders. I believe my answer here could also be helpful.

Send Firebase Cloud Messaging notification to users by user property

I'm trying to send out an FCM message to a specific set of users (or really, a single user) based on a specific user property but looking through the FCM HTTP API I can't seem to find a way to do that. I can send to users via topics, registration tokens, and device group notification keys, but I don't have any of that infrastructure set up in the near term. I know this functionality exists as you can send such a message via the UI, but I'm not seeing how to do it in the API documentation as yet.
There is currently no parameter that you could use to specify a user property (or even for user segments) that will serve as a target for the FCM API to send the message to.
As you've already searched, the only targets possible are single/multiple registration tokens (to and registration_ids), topics, conditions, and device groups (notification_key).
The option you're looking for is currently only available when using the Notifications Console.

Resources