Exposing Firebase Keys to the internet - firebase

If I am storing something in Firebase and the key of that is something like -L1gMGVKaj-qU8O05eeT, is it safe to create public URLs like http://example.com/item/-L1gMGVKaj-qU8O05eeT?
Is it safe to distribute internal push keys to public, provided that proper database rules are in place?

There is no security issue in using push ids/keys in public URLs provided that your credentials (e.g. Server Keys or Service Accounts etc) are safe and not viewable.
However if you want that users should not be able to guess other push ids/keys then you should check this Answer

You can create your url using the keys generated by firebase on basis of timestamp. But it will not be user friendly. you can convert the key to unix timestamp and then use it for url. Look at this answer

Related

What to do about this warning email from Google: Publicly accessible Google API key for Google Cloud Platform?

As the title states, I've gotten this email for both projects I've made public on Github. One is a landing page for a local business and the other is a CRUD app I have on the App Store; both of which are using Firebase as the backend.
Is the API key being visible on Github such a security risk?
I've done some research after following the instructions in the email to restrict my API and have heard that you cannot make web service requests with a restricted API key.
I just want to show my repos for the projects for the application process and obviously don't want anything bad to happen with them by doing so.
Aren't Firebase APIs meant to be public?
If so, is it just my database rules that need to be stronger/more verbose?
If any more context is needed, please let me know!
Cheers!
NOTE: I'm still very new to programming so a lot of this is over my head
For Firebase apiKey in a web app you are intended to make this key public, so you should ignore this email -- see: https://stackoverflow.com/a/37484053/771768
Hopefully Best practices for securely using API keys helps.
I'm uncertain as to what you're doing specifically that's resulting in the email but it is warranted.
Please be very careful with API keys.
As the name suggests, these are like keys in that they unlock access to stuff. With digital keys, the additional challenge is that, once obtained, infinite copies of the key may be distributed (and these are usable until the API key is revoked).
There are (often) other (complementary|alternative) ways to authenticate APIs but, as I think you've discovered, sometimes you are required to use API keys.
In the case where they're required, you should endeavor to use complementary authentication mechanisms too in order to try to mitigate overuse and you should continue to be very judicious in your publication of these keys.
I suspect you should not be including (any) keys (ever) in your GitHub repos.
One rule of thumb is that vendors (like Google) use API keys as a way to limit access to (often paid) resources. If the vendor is giving you a key, they're often (not always) using the key as a way to determine how to charge you for an API too. If you're giving the key to others, you're giving other people the possibility of potentially incurring charges on your behalf.
I don't wish to scare you but I would like you to leave this question being very cautious when using keys even if only this causes you to read up more on the consequences of using them.

What Firebase Storage reference should be saved to RTDB/Firestore?

I'm trying to determine the best way to reference a Firebase Storage (Google Cloud Storage) file in a direct-read database like Realtime Database or Cloud Firestore. Since a read operation to this database does not benefit from a backend that can issue tokens and cache image URLs, it is not clear to me what the most performant way is to store these references.
I have come up with a few options and none of them are a clear winner.
Store a path like /images/foo.jpg to the database, and use Storage Client SDK to generate a tokenized path with storage.bucket().getDownloadURL("/images/foo.jpg").
Pros: Secure & simple.
Cons: Network call for every single image you want to display hurts performance considerably.
Store a tokenized path like https://firebasestorage.googleapis.com/v0/b/storage-bucket-823743.appspot.com/o/images%2Ffoo.jpg?alt=media&token=c6da1e33-f3ff-41e2-a6f0-bdb475a2f6d9 with a super long TTL.
Pros: No extra fetch on the client.
Cons: long string stored in expensive RTDB. What if that token is revoked by mistake? The database is now broken.
Store a path like /images/foo.jpg to the Database and use public storage rules. Reconstruct into a custom static URL like https://firebasestorage.googleapis.com/v0/b/storage-bucket-823743.appspot.com/o/images%2Ffoo.jpg?alt=media
Pros: Tiny database use, no extra client fetch, explicit public access, no token to lose.
Cons: URL encoding is super flaky, Storage could change their URL format and we'd be out of luck.
So, those are the options I've come up with, and there may be more. Any suggestions for how to solve this issue? The issue is unique because the Firebase databases don't have the benefit of a custom server to handle a token/caching layer to resolve this problem.
There is no single "best way" to store these paths. It all depends on your use-case, your preferences, and the context in which you're implementing it.
I typically use :
If I need access to the files to be secured, I store the image path (like in your #1), and then use the Firebase SDK to access the file.
If I don't need access to the files to be secured, I store the image path and the download URL. This way I can find the image easily based on the path, and use the download URL in non-secured clients.
The con's you mention for these are simply not affecting me. I'd recommend you take a similar approach, and report back when the problem actually occurs.

Firebase real-time database (Get other user info based on UID) with REST

I have an app created with Firebase real-time database and I've created a few users, all with REST methods found here: https://firebase.google.com/docs/reference/rest/auth/
I can get the user info for the current user, but I would like to have a REST endpoint to fetch all the users (UID, email, displayPhoto, name, etc) or at least a user by UID. I haven't found this method in the link above.
I know that there is an SDK to do that (https://firebase.google.com/docs/auth/admin/manage-users), but I would like to do this with REST since the full app is using REST.
Does anybody know if this is possible and has the rest endpoint?
Thank you!
There is no public REST API to get a list of all users. The reason for this is that getting a list of users is considered a sensitive operation, and allowing that from client-side code would be risky.
The common way to implement your use-case is to build your own endpoint, either on a server you already control, or with Cloud Functions. There you can use the Admin SDK to get the list of users, and then return that to your caller. Make sure to limit what data you return and to properly secure that endpoint though, as otherwise you'll be putting your user's information at risk.

Firebase Cloud Functions Secure HTTPS Endpoints with API key

I've looked at a few places, Including this post and the firebase panel
Is there no way to use these api's to secure these endpoints using an api key you create per client who uses your cloud functions?
I'm able to block every one putting a restriction on the Browser key, but I would like to create a new api key, and use that as a way to authenticate my endpoint for various clients.
Creating a new api key, and using that as a parameter in my query doesn't work (don't now if I'm doing anything wrong)
Is there a way to do this?
Option 1: handle authentication within the function
https://github.com/firebase/functions-samples/tree/master/authorized-https-endpoint
Adapt above to use clients/keys stored in firestore
Option 2: Use an an API Gateway
Google Cloud Endpoints (no direct support for functions yet, need to implement a proxy)
Apigee (higher cost, perhaps more than you need)
Azure API Management (lower entry cost + easy to implement as a facade for services hosted outside Azure)
there are more..
The above gateways are probably best for your use case in that the first two would let you keep everything within Google, albeit with more complexity/cost -- hopefully Endpoints will get support for functions soon. Azure would mean having part of your architecture outside Google, but looks like an easy way to achieve what your after (api key per client for your google cloud / firebase functions)
Here's a good walkthrough of implementing Azure API Management:
https://koukia.ca/a-microservices-implementation-journey-part-4-9c19a16385e9
Not to achieve what you are after, as far as firebase and GCP is concerned your clients is your specific business problem.
One way you could tackle this (with the little information that is provided);
You need somewhere to store a list of clients + their API key (I would use firestore)
For the endpoints you want to secure with a client-specific API key you can include a check to confirm the header exists and also exists in your firestore client record.
Considerations:
Depending on your expected traffic loads and the the number of firestore reads you'll be adding, you might want to double check this kind of solution will work for your budget.
Is the API-key type solution the only option you must go for? You Could probably get pretty far using the https://github.com/firebase/firebaseui-web and doing user checks in your function with no extra DB read required. If you go down this path most of the user signup/ emails / account creation logic is ready to go.
https://firebase.google.com/docs/auth/web/password-auth#before_you_begin
Curious to see what some other firebase users suggest.

Firestore - security rules for users within companies

Our current Firestore structure is as follows:
Currently we are not using any subcollections
Users have list of companies to which they belong
Every project is connected only with 1 company
Project belongs to a company, when in companyId field is written that company UID
My 1st question is how we can specify security rules defined by this database? Is there some best practice approach?
Our first idea was to do this:
match /databases/{database}/documents/projects/{projectUid}/{document=**} {
allow read: if
(/databases/$(database)/documents/projects/$(projectUid)/companyId) ===
(/databases/$(database)/documents/users/$(request.auth.uid)/companyId)
}
But according to the documentation this would mean that we would have for each read basically 3 reads (2 queries for security and 1 real read from DB). This seems like a waste of queries.
Is there a better approach than this?
We were thinking about changing to subcollections:
at the end we would have in root collections 'companies' and 'users' (to store all users details)
projects would be subcollection of companies
pages would be subcollection of projects
...etc
and companies would contain list of users (not the other way around like now) - but only list, not user details
This way we can use similar approach as from the doc, where each match would contain {companyId} and in allow statement we would use something like
match /databases/{database}/documents/companies/{companyId}/projects/{projectId} {
allow read: if
exists(/databases/$(database)/documents/companies/$(companyId)/users/$(request.auth.uid));
}
Thanks for any recommendations on how to build it in the most scalable and especially most secure way.
Have you considered adding a user's company ID as a custom claim to their profile? That way no additional reads are needed in your security rules.
Since setting these claims requires the Admin SDK, it will require that you can run trusted code somewhere. But if you don't have your own trusted environment yet, you could use Cloud Functions for that e.g. based on some other action like writes to your current Firestore structure.
Adding an answer to Frank.
Borrowing from other API SDKs such as microsoft graph, typically to make a resource request you start by initializing a Client object with an authentication token representing the scope/rights of the user. For example:
const client = new SDKClient(my_auth_token);
The client constructor would have a token validation step on claims. You can then make REST calls such as
const response = await client.someEndpoint({ method: 'POST', body: my_object });
I suggest rather than using the admin SDK for read/write to your firestore, you use the regular firebase nodejs client. To restrict access with security rules, pass a firebase JWT token into this custom SDKClient class with the token that you obtain from the header of your requests. In the constructor, initialize a new firebase 'app'. Because a regular firebase client is
subject to security rules, this will do what you're looking for.
Some example code has already been offered in this answer.
I should add that according to this firebase doc there is a 'warning' to use the admin-sdk server-side, but I'm not sure I see why.
One approach I've thought of for something similar that we are working on, that is, private chatrooms where only certain users have access, is to encrypt all messages with an on-server key, and only grant read access for that key to certain users. That way the extra read only has to occur one time, just when getting the key for the first time, then normal reads with no additional security rules are fine, as an attacker wouldn't be able to do anything with them since they are encrypted and they don't have access to the key.

Resources