How can I secure Firebase Real-Time Database Rest API? - firebase

Let me first explain the scenario:
1. Realtime Database looks like this:
{
"Notifications": {
"348199": 1, //Every change will increment one (1, 2, 3, 4, etc...)
"737119": 1, //Every change will increment one (1, 2, 3, 4, etc...)
"899173": 1 //Every change will increment one (1, 2, 3, 4, etc...)
}
}
2. The client side (Android app):
Let's suppose the current user id is (348199), I will create a listener that observes any change on this node (348199), When the value is changed, I'll tell the user he has a new notification.
3. The server side (The API is written in PHP language):
Let's suppose someone sends a friend request to this user (348199), First I'll add a notification inside the Notifications table that exists inside MySQL and after added successfully I'll change the value of (348199) in the Real-Time Database
4. The URL that will change the value (Patch request)
https://IHideIt-default-rtdb.firebaseio.com/Notifications.json
The question is:
I want to reject the write request inside Firebase Real-Time Database if was coming from a browser, postman, my android app, etc..., How can I secure the URL above? (I want only my API can write inside Firebase Real-Time Database)

You can reject write operations from client SDK using security rules. The Admin SDK can still read/write the database from server side. Try the following rules:
{
"rules": {
"Notifications": {
"$uid": {
".write": "false",
".read": "auth.uid == $uid"
}
}
}
}
These rules will allow users to read their own notifications only (if you are using Firebase Auth) and not write the database.

Related

connecting firebase Realtime database with chrome extension

I'm beginner and I'm developing a chrome extension that shows data received from my firebase realtime database. It does not need Login, or any personal information.
I'm trying to use REST API. Until now I have been in a test mode so I opened access to data for all. But Google keeps mailing me that I have to change the access rule because it is dangerous.
My present access rule is this:
{
"rules": {
".read": true,
".write": false
}
}
Now, I fetch data from https://<project name>.firebaseio.com/<database name>.json. But if I change the rule, the access will be denied.
So I want to add some key to the url and change access rule according to it so that I can fetch data from the url. For example,
https://<project name>.firebaseio.com/<database name>.json?some_key=<some_key>.
I do not need personal keys so I want only one key just like when we get information from open APIs. For example, when I use weather api, I get my api key from the host and adds the key to url.
Is this possible? If it is, I wonder two things.
the key that I can use (for example, realtime base wep API key)
access rule of realtime database
You can't pass parameters to the Firebase database like this:
https://<project name>.firebaseio.com/<database name>.json?some_key=<some_key>
But if you change the order of the values, it suddenly becomes possible:
https://<project name>.firebaseio.com/<some_key>/<database name>.json
We then change the security rules to only allow access when somebody already knows the secret key:
{
"rules": {
".write": false,
"$some_key": {
".read": true
}
}
}
Now the secret key is part of the path of the data, and only a user that already knows the key can read the data for that key.

Access denied on Firebase Realtime Database security rules simulator

It is hard for me to understand why the simulator puts me off for this input
I supplied this auth token body:
{
"account": "7xms2zm6noz03f2mvn",
"playerId": "d3221a31-263c-4629-92fb-6cac89b67088"
}
I am using custom authentication to supply the above blob.
Please see the attached screenshot for how the simulator treats this:
This is my database tree:
What am I missing?
It appears that when you use the simulator, the "auth token payload" is actually at the auth level, not at the auth.token level.
So, you need to simulate the auth.token.account claim like this:
{
"token": {
"account": "7xms2zm6noz03f2mvn",
"playerId": "d3221a31-263c-4629-92fb-6cac89b67088"
}
}
You can tell it is (apparently) misnamed because if, for example, you select the "Google" provider, then provider and uid are both at the top level of this blob (which can't be modified), and that is where you would expect to find them.
Likewise this image from this blog post shows the token block as a sub-block in the simulator.

Angular+Firebase: How to best secure your database on a landing page (no authentication required)

I built a simple landing page with Angular and offer visitors who may be interested in the service to send their data through a standard sign-up form. The data are stored in a firebase database.
What would be the recommended approach to best protect my database knowing that:
my firebase url is public (currently stored in the javascript)
the page is public (so anyone can 'write' to the database)
So far I've added the following rules to my firebase console:
{
rules: {
".read": "auth != null", // nobody can read
visitors: {
".write": "!data.exists()", // nobody can modify existing data
"firstname": {".validate": "newData.isString() && newData.val().length < 40"}, // only string with less than 40 characters
"lastname": {".validate": "newData.isString() && newData.val().length < 40"}, // only string with less than 40 characters
"email": {}, // no rules
"message": {".validate": "newData.isString() && newData.val().length < 500"} // only string with less than 500 characters
}
}
}
Question 1: Are these rules good enough to protect my database or should I consider adding additional ones?
Question 2: Should I consider hiding my Firebase URL on a backend server? My idea would be to put the firebase url in a php file on the server; To update the database with new visitor data, the app would first make an ajax call to my server through the $http service, get the firebase url back, to then update the firebase database. In that case the firebase URL would not be public anymore.
What do you think? Many thanks
These rules validate the format of the data that can be written. They in no way limit who can write this data. Whether these validation rules are sufficient for your security requirements, only you can tell.
Some developers wrap the Firebase Database API with their own server. But most developers expose the database directly to the users of their app and then put their server-side logic behind the database, as described in this page and diagram:
Those developers typically end up with stricter security rules than you have now though.

Firebase Security Rules Block Writing to Firebase

Note: This question is tagged polymer because the Polymer library is used to generate the Javascript.
This question is about two different but related problems dealing with Firebase security. Problem 1 and Problem 2 seem to suggest opposite realities and opposite results when trying to get the Firebase security rules to work. (FWIW: Here is my prior unsuccessful attempt to write this question.)
Here is the live code in a JSBin.
http://jsbin.com/hinehoyigo/edit?html,output
Problem 1
Firebase Security Rules Block Writing to Firebase
Instructions
Enter your firebase ID. Example: "hot-sauce-123"
In a new tab or window, go to your firebase and open it
Allow anonymous auth:
Your firebase
> Login & Auth
> Anonymous tab
> Check "Enable Anonymous User Authentication"
Apply no security rules: Security & Rules >
{
"rules": {
".read": true,
".write": true
}
}
Return back to jsbin
Select "anonymous" as auth provider: dropdown menu > anonymous
Click button labeled "login"
Verify login status per fields to right
Open your console: Chrome > View > Developer > Developer Tools
Click button labeled "Print User to console"
Double-check login status by verifying user object printed to console
Click button labeled "Print Firebase to console"
Verify correct firebase by checking Firebase URL printed to console
Click button labeled "Write to Firebase — Plain Url"
Check your firebase data; notice no write occured
Click button labeled "Write to Firebase — .json Url"
Check your firebase data; notice successful write
Distinguish the two write attempts because former attempts to write {"quux":"baz"};
latter attempts to write {"jquux":"jbaz"}
Add security rules: Security & Rules >
{
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid === $uid",
".write": "auth != null && auth.uid === $uid"
}
}
}
}
Click again the button labeled "Write to Firebase — .json Url"
Check your firebase data; notice write NOW FAILS
Conclusion / Problem Statement: Firebase security rules block writing to Firebase.
Problem 2
Simulator blocks .json URL from being used (as required above)
Instructions
Open your Firebase
Verify security rules are in place (see above step #19 in Problem #1)...
Open your simulator: Vertical Navigation Menu > Simulator
Check radio button labeled "Custom Auth"
Custom Auth input field should read something like
"{ provider: 'anonymous', uid: 'ag69g401-f097-4171-bca4-927fd1a6e1f3' }"
Click button labeled "Authenticate"
Verify little green check mark is next to radio button labeled "Custom Auth"
Go to section 2
Click tab labeled "Write"
In input field labeled URL, enter "users/ag69g401-f097-4171-bca4-927fd1a6e1f3/foo/bar"
Verify the user ID in the URL path you just entered, matches the uid value in the above Custom Auth field
Click button labeled "Simulate Write"
Notice response says something like:
Attempt to write {"key":"value"} to /users/ag69g401-f097-4171-bca4-927fd1a6e1f3/foo/bar with auth={"provider":"anonymous","uid":"ag69g401-f097-4171-bca4-927fd1a6e1f3"}
/
/users
/users/ag69g401-f097-4171-bca4-927fd1a6e1f3:.write: "auth != null && auth.uid === $uid"
=> true
Write was allowed.
Notice: This result is the opposite result from above steps #14 and #15 where the plain URL (without .json appended) failed to write
Append ".json" to the URL so the input field reads something like
"users/ag69g401-f097-4171-bca4-927fd1a6e1f3/foo/bar.json"
Click button labeled "Simulate Write"
Notice response says something like:
Invalid path. Simulation aborted.
Notice: This result is the opposite result from above steps #16 and #17 where the .json appended URL was the only one that worked;
Notice these simulator tests suggest the opposite results than those tested from the live code in above Problem #1
Problem 1
You're using the Polymer firebase-auth element to authenticate with Firebase.
<firebase-auth id="firebaseLogin"
user="{{user}}"
...
Under the hood this calls the Firebase.authAnonymously() method, which authenticates the current session in the Firebase JavaScript SDK.
You're writing to Firebase using:
computeFbTargetJson: function(f) {
return f + '.json';
}
and
this.set('$.ajax.url' , this.fbTargetJson);
this.set('$.ajax.body' , JSON.stringify({
jquux: 'jbaz'
}));
this.$.ajax.generateRequest();
This is doing an AJAX request to Firebase, which means that you're using Firebase's REST API. But since this is a regular HTTP call, it will not "inherit" then authentication from the Firebase JavaScript client.
To authenticate your REST request, you will need to pass in an auth parameter as documented in the Firebase documentation. You can get the token from the Firebase reference using ref.getAuth().token.
Problem 2
Each element in your Firebase database is accessibly by a unique URL. You can either access it with one of the Firebase SDKs or by directly accessing the REST API that Firebase exposes.
From what I can gather from the jsbin, you're writing to Firebase using:
computeFbTargetJson: function(f) {
return f + '.json';
}
and
this.set('$.ajax.url' , this.fbTargetJson);
this.set('$.ajax.body' , JSON.stringify({
jquux: 'jbaz'
}));
this.$.ajax.generateRequest();
This is doing an AJAX request to Firebase, which means that you're using Firebase's REST API. The first paragraph of that API documentation explains how Firebase database URLs map to the REST API:
We can use any Firebase app URL as a REST endpoint. All we need to do is append .json to the end of the URL and send a request from our favorite HTTPS client.
So in order to access any data in Firebase using the REST API, you append .json to the URL of that data.
When you're using the Firebase Simulator, the operation is similar to using Firebase's JavaScript SDK. In that case, you should not put the .json suffix at the end of the URL.
So given a Firebase database at https://mine.firebaseio.com/, say you have a user profile at /users/de01fc8104.
You can access that record in the simulator or with a Firebase SDK as https://mine.firebaseio.com/users/de01fc8104
To access the record using the REST API, the URL is https://mine.firebaseio.com/users/de01fc8104.json
Summary
Both of your problems are caused by the fact that you're using two different ways of accessing Firebase: the JavaScript SDK and the REST API. While these two methods can be used together, you will have to ensure you provide the information that is needed. In general you will have an easier experience if you stick to using one way of accessing Firebase from a single client.
#FrankvanPuffelen's answer in Polymer-speak translates into:
Use the <firebase-collection> element's .add() method to write data to Firebase from the client while under security rule enforcement. It's more efficient from a code standpoint as firebase-collection handles all the necessary auth tokens for you automatically.

Fetching custom fields from auth object in Firebase

I am trying to invalidate/revoke client's auth token when they sign on a different device. Initial auth-token is supplied through our server and not firebase (but uses the same secret key, hence works with firebase too).
For each user we save an associated password which gets passed as part of auth-token, when user switches the device - we issue a new password from the server and compare password to invalidate token on server. Firebase connection however still persists.
I am trying to store passwords on firebase for each user. This can then be updated every time we change the password at the backend and use it to invalidate the firebase token as well. However, I am not able to extract password from the auth object. Any ideas?
This is my firebase security rule.
{
"rules": {
".read": root.child('passwords').child(auth.uid).val() == auth.password,
".write": root.child('passwords').child(auth.uid).val() == auth.password
}
}
Surprisingly, none of the custom field in auth object are present.
Whatever you put in the custom auth object would be available in security rules and would be part of the authData returned after authenticating on the client. When you're creating the JWT, you have to follow Firebase's specific rules, which are outlined here. The important part being that a d key contains the authentication data you want to make available. Here's basic example of what the payload might look like.
{
"v": "0",
"iat": 1448391639,
"d": {
"uid": "user1234",
"password": "mySecretPassword"
}
}
http://jwt.io/ is a great tool for quickly creating JWTs for testing. If you use the payload above in conjunction with your Firebase secret, you should see the same payload within your auth data and security rules.
f.authWithCustomToken('eyJhbGciOiJIUz...', function(err, data) {
if (err) {
// Handle error
} else {
console.log(data.auth); // {uid: "userId1234", password: "mySecretPassword"}
}
});

Resources