authentication with firebase Realtime Database without user - firebase

I have a firebase realtime database with read/write to all, however I don't have any user and don't intend to auth user. The data is written by a event listener and scheduler(java), my html ui application supposed to read data only.
How should I do to secure my db?
With development(unsecure) mode, I can use any http client to write/read data? How do I pass authentication after I secure it?

To allow only your web site to read, and only your backend to write, you'll want to combine security rules with Firebase App Check.
Security rules
First in security rules, you'll do:
{
"rules": {
".read": true,
".write": false
}
}
This allows everyone to read your entire database (we'll limit it to your app later), and allows nobody with a client-side SDK to write to it.
I strongly recommend adding some additional logic in the rules to limit how people can read the data though. For example, if your code first reads a list of users, and then shows the posts from a selected user, modify your rules to only allow that specific path, and reject anything else.
App Check
Now with the rules in place, you'll want to start using App Check to reduce the abuse you get from people taking your configuration data and calling the API on their own.
App Check is no guarantee that this can't happen anymore (especially on web), but it definitely increases the work a malicious user has to do.

Related

Why does Google Firebase Cloud Firestore has "write", "update" security rules, while we should not allow users to write directly on database?

I'm implementing an application that using Google Firebase Cloud Firestore. Because my application has a lot of small write requests, it will be very costly if I use Firebase Cloud Functions in the middle. Therefore, I asked a question on Should we allow users to write to database directly. All responses said that I should not do so. Then, why does Google Firebase Cloud Firestore has "write", "update" security rules? Users should not write to the database anyhow.
EDIT (responding to DIGI's comment & answer):
From the answer, it seems like we can use the firebase rule instead. Then, how should we correctly use them?
For example, my app is recording user's location when they turn on the map & update it to the database so that user's friends can see it in real-time. How should I ensure that the data is in the form {longtitude: double, latitude: double, timeStamp: TimeStamp}, and the user doesn't change any other few in the document?
This is a common misconception from standard MySQL databases and similar where a server acts as a layer of logic and sits between the client and database.
Firebase still has this layer of logic in a simplified version known as security rules which allow basic read and write operations from the client. The design is by shifting computing power from the server backend onto the client instead, saving in server computing costs on their backend by distributing that requirement to users' devices.
The concerns listed in SE are from people who are unaware of the limitations and restrictions you can place inside rules and your project to control what can be requested with firebase and your app.
To clarify, Firebase does not hide your database keys, they are easily accessible and yes, a user can theoretically make a client using your database backend. but so long as rules are in place, conditions defined, Cors configuration, and app origin settings are enforced, you can prevent all of this.
For multiple writes, Realtime is ideal since it can handle data faster and more cost-effective than Firestore writes.
So my example will respond with the intent of realtime database per your edit.
{
“rules”: {
“location”: {
“$uid”: {
“.validate”: “newData.hasChildren(['longtitude', 'latitude', 'timestamp']) &&
newData.child('longtitude').isNumber() &&
newData.child('latitude').isNumber() &&
newData.child('timestamp').val() <= now”,
}
}
}
}
I highly suggest getting familiar with some core concepts
https://medium.com/#juliomacr/10-firebase-realtime-database-rule-templates-d4894a118a98
Full documentation here: https://firebase.google.com/docs/reference/security/database

Firebase Security Open Access

My android wallpaper app is connected to Firebase Cloud Firestore. It doesn't have any user authentication because I want the user to be able to use it without fuss. To do this, it must be in open access, meaning, the user is allowed to read and write. This is dangerous as he can also edit and modify the data if he knows the project id. The project id is visible in the url of the app so this is not really a good choice. Closed access is also not an option for obvious reasons.
Is there anything I can do to protect my data without need of a user authentication? I would love to see the code needed for the Cloud Firestore and Storage to protect the data. I want the user to read only and I, as the owner, should be the only one who could write. Please refer to the images attached. Thanks a lot for taking time to answer my questions.
My data structure in Firebase Cloud Firestore:
Securing your data with Security Rules
Firebase Cloud Firestore, Firebase Realtime Database and Firebase Cloud Storage are secured by their own Security Rules. They provide access control and data validation in a simple yet expressive format and allow you to control access to documents and collections in your database.
To build user-based and role-based access systems that keep your users' data safe, you need to use Firebase Authentication with Cloud Firestore Security Rules.
Your specific use case
I assume that you store your data in Firebase Cloud Firestore and the wallpapers in Firebase Cloud Storage. The user then gets a document with a link to download a wallpaper and maybe also can upload their own wallpapers to the database.
The dangers of an open database
As you mentioned allowing all reads and writes to your database in a production app is very dangerous. Obviously, anyone with your database reference will be able to read or write data to your database. Unauthorized users could destroy your backend and there are also possibilities that costs could increase exponentially. Therefore this is not recommended. There always should be some mechanisms preventing these scenarios.
Recommendation: Using anonymous authentication first and connect later with existing Identity Providers
I recommend that you use Firebase Authentication to create and use temporary anonymous accounts to authenticate with Firebase. These temporary anonymous accounts can be used to allow users who haven't yet signed up to your app to work with data protected by security rules while not being in the way of your user experience. If an anonymous user later decides to sign up to your app, you can link their sign-in credentials to the anonymous account so that they can continue to work with their protected data in future sessions.
You could also enable Google-Sign-In, Facebook Login, Twitter, GitHub, Microsoft, Yahoo, etc. to let users authenticate in a very fast and easy way without compromising on a security standpoint if using regular password authentication is not what you want (from a UX standpoint). FirebaseUI makes it really easy to add these to your existing app. Check out the official documentation on this topic.
Implementing Cloud Firestore Security Rules
The official documentation on this is really great on how to get started with Cloud Firestore Security Rules.
However these security rules could work for you:
Allow read access to all users to the root (Not recommended because this can create huge costs in production). Users don't have write (create, update, delete) access. In this case you can edit your data via the Firebase Console. Choose either option 1 or option 2 for your project.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Option 1: Matches any document in the 'root' collection.
match /root/{rumiXYZ} {
allow read: if true;
allow write: if false;
}
// Option 2: Matches any document in the 'root' collection or subcollections.
match /root/{document=**} {
allow read: if true;
allow write: if false;
}
}
}
The {document=**} path in the rules above can be used to match any document in the collection/subcollections or in the entire database. This should however not be necessary for your use case. Continue on to the guide for structuring security rules to learn how to match specific data paths and work with hierarchical data.
Don't forget to secure your Firebase Cloud Storage too!

Secure Firestore database using rules. Check authentication. Is it enough?

I'm new to Firebase and trying to understand database rules. I'm using Firestore.
I have a database that basically needs to be read by all users, and also write. All users can see the documents in the database, and with certain actions they change certain fields. In certain cases they will detele certain old expired documents.
Now, I understand that I cannot leave read and write open to all, since this is not secure. So I am using authentication, I will anonymously authenticate the users, so that only authenticated users have access.
I understand this does the job:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid != null;
}
}
}
Now, my question is, is this enough? I'm not a hacker, so I don't exacly know how a hacker would or could hack and detele/change stuff in my database, but does this mean that only changes can be made to the database through using the app? Could someone still hack this if they aren't using the app, and authenticate in some other illegal way.
Thanks so much for the help, I've tried to read to get to the bottom of this, but haven't managed.
Firebase security rules can't limit access to a single app. All of the APIs are all available for public use for anyone who has an internet connection. They are all documented right here: https://firebase.google.com/docs/reference/rest/auth
The purpose of Firebase Authentication is to make sure that individual users have their individual access controlled appropriately. As soon as you let users create accounts using anonymous or email auth, they will have full access to all documents in the database with these rules. So, what you have right now is not really "secure" by most definitions of that word. You will have to decide if this is "secure" enough for your purposes.
You are also likely to get an email from Firebase saying that your rules are insecure. It's not a good idea to use /{document=**} like this, which matches all documents, which might not be what you intend. Minimally, you should call out the individual collections that you want users to access instead of using this global wildcard.
Does this mean that only changes can be made to the database through
using the app?
Anyone that can get your Firebase config elements could write a simple HTML page using the JavaScript SDK and try to interact with your Firestore backend. Note that it is not difficult to get your Firebase config elements, see at the bottom for more details.
This is why it is of upmost importance to implement a set of security rules if you want to protect your data.
Now, it is important to note the following point about Firebase Authentication and “registered“ users:
You should note that anyone can “create a new user in your Firebase project by calling the createUserWithEmailAndPassword() method or by signing in a user for the first time using a federated identity provider, such as Google Sign-In or Facebook Login” (if these identity providers are activated, of course). See the doc.
So, again, with your Firebase config elements, someone can easily build an HTML page that calls the createUserWithEmailAndPassword() method.
This means that if you want to limit the access of your app to some specific users just by using allow read, write: if request.auth.uid != null in your Firestore security rules, it is not sufficient.
One possible approach is to use Custom Claims. You can for example, set a specific claim to all your authorized users (e.g. authorized = true) and adapt your security rules to check the existence of this claim in the user token.
Note: How to find the Firebase config elements of a web app?
It is not really difficult to find the Firebase config object. Just look in all the HTML or JS files composing the app for the apiKey string.

Firebase Cloud Firestore restrict user access (Banking Application)

I am working on a banking app using firebase cloud firestore. I have already set the rules like so:
// Allow read/write access on all documents to any user signed in to the application
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid != null;
}
}
}
My database is structured like this:
/consumers/{consumer_id}/transactions/{transaction_id}
the {consumer_id} will contain the account balance for that consumer along with other details and the {transaction_id} will contain the details about each transaction.
So if any authenticated user wanted to say, withdraw money they can do so using the android app/Web app. The problem is, can that same user access the database (eg: update their account balance) using their credentials with the REST endpoints without my knowledge? If so how do I prevent them from doing so?
There is no way to limit access to Firestore to just users who are using your app. Anyone who has the configuration data for your Firebase project, can call the APIs in that project. And as soon as you publish your app, you're sharing the configuration data with those users. So you'll have to assume that some malicious user(s) will at some point call APIs on your project without using your app.
For this reason you should enforce all business rules that you have in a trusted environment, such as your development machine, a server you control, Cloud Functions, or... server-side security rules. Since no user can access any of these, even if they run their own code, they'll be forced to adhere to your business rules.
Some examples:
Each transaction document probably contains the UID of the user who is posting that transaction, and of course users should only be able to post transactions with their own UID. You can enforce this in security rules with something like:
match /databases/{database}/documents {
match /consumers/{consumer_id}/transactions/{transaction_id}/ {
allow write: if request.resource.data.posted_by == request.auth.uid;
}
}
So now anyone (no matter if they're using your app or not) can only post transactions if that document contains their own UID. You'll probably want to verify a bit more, such as whether there is even a account document for them, and maybe whether you've verified their account in some way. All of these can typically be done from server-side security rules.
For more on this, see the documentation on accessing other documents in security rules, the pro-series video on building secure apps, and this video on security rules.
Since you keep the balance of each account in their parent document under /consumers/{consumer_id}, you'll need to update that document whenever a transaction is posted under it. While this is possible from within security rules, it's going to be quite involved. It's going to be easier to perform this update of the balance in server-side code.
A good solution for this is to run the code that updates the balance as a Cloud Function that gets triggered whenever a transaction is created (and/or updated if you allow that). Since this code runs in a trusted environment, you can be sure only you can modify it, and thus it can safely update the balance for the new/modified transaction.

Firebase Storage Security Rule : Check if another object exists / check object's metadata

In Firebase storage security rules (not realtime database), is there any way to perform a check if another object exists at path, or another object's metadata exists?
Some background
Currently my storage security rules are set up so that users only have read access, and not write access to their /users/{userId}/ paths.
I have an admin cloud function that saves a file to /users/{userId}/necessary-file.pdf. And I don't want users to be able to modify or write this file and only cloud functions to have the right to do. To achieve this I think I can match for the filename like :
match /users/{userId}/{fileName} {
allow write: if !fileName.matches("necessary-file.pdf")
}
Question
Is there any way for me to only allow users to write some-other-file.pdf if they already have a necessary-file.pdf at the same path (or even somewhere else if that works better). All while still disallowing them to write necessary-file.pdf.
So is there any way for me to do something like this pseudo-code? :
match /users/{userId}/{fileName} {
allow read: if request.auth.uid == userId;
allow write: if (!fileName.matches("necessary-file.pdf")) && ("necessary-file.pdf".exists())
}
As an alternative, I can have my cloud functions write a metadata to necessary-file.pdf and check for that too. is there any way I can perform something like this pseudo-code? :
allow write: if "necessary-file.pdf".metadata['canUserWrite'] == 'yesUserCan'
Finally
What's really cool about this is that, if this is in any way remotely possible, it can be used to communicate between firebase database and firebase storage rules in a not-so-realtime way. (referring to this question here) A cloud function can listen for changes in the intended field in realtime database, and write a file to firebase storage, which firebase storage can check for.
Firebase's Cloud Storage security rules can only access information about the current request and file. They don't have access to the full storage system, so can't check whether another file is present.
The reason for this is that the rules are evaluated in memory for every request. Providing access to Cloud Storage for other objects would slow the performance down, making the system unscalable. That same reason explains why you can't access the Firebase Database from the security rules.
If you want some control like this, you'll want to look in Cloud Functions for Firebase. If you have your users upload their files into a "staging" area, you can have a Cloud Function validate whether they met all prerequisites and only then move the file into the actual location (making it available for further processing or for clients to see).
(Another Solution) Restricting Storage Access with Auth Claims
Cloud Storage Rules has access to auth info for the request user. By setting up a check during the authorization process an auth property can be added for later access validation in storage rules.
Original Question:
Is there any way for me to only allow users to write
some-other-file.pdf if they already have a necessary-file.pdf at the
same path (or even somewhere else if that works better). All while
still disallowing them to write necessary-file.pdf.
Yes, this could be done by checking an auth.token.
Example flow for a Web App w/ Google Signin:
Create a Cloud Function to set a custom claim that checks for the existence of that file against the current auth user.
Upon success of the user authorizing via Google call that cloud function for it to set a value to the auth object.
Check auth.token in the storage rule.
Web Example for Custom Claims:
https://firebase.google.com/docs/auth/admin/custom-claims?hl=ro#examples_and_use_cases
Example Storage Rule:
https://firebase.google.com/docs/storage/security/rules-conditions?hl=ro#group_private

Resources