Due to the thin AngularFire documentation and the differences between it and the default web documentation for Firebase, I'm a little lost on how best to secure Create, Read, Update, and Delete operations with users.
In short, say I have an application that manages stores. Users can be owners of the stores or patrons. Owners should read and edit their own stores in their view and patrons should read all but edit no stores in their view.
I'm concerned about the security of suggested methods by Firebase docs such as
So for example, we could have a rule like the following to allow users
to create comments as long as they store their user id with the
comment:
{
"rules": {
".read": true,
"$comment": {
".write": "!data.exists() && newData.child('user_id').val() == auth.id"
}
}
}
To me, this means that I could hack my application's data by simply passing in my victim's user id when I want to post a comment as them. Am I wrong?
I've read the security documentation thoroughly, several times. I think I need further explanation here. Identifying by a client-exposed parameter is the only method I can find so far.
In the example shown here, auth refers to the authenticated user's token data. This is a special variable set by Firebase during auth() events, and thus not something you could hack at the client. In other words, you would only be able to write a comment if you set the user_id value to your own account id.
The contents of the auth object depend on how the client authenticates. For example, SimpleLogin's password provider puts the following into the auth token: provider, email, and id; any of which could be utilized in the security rules.
It's also possible to sign your own tokens from a server, and of course the sky is the limit here.
But the bottom line is that the token's internal values are provided by a trusted process and not by the client, and thus cannot be altered by a user.
Related
It appears that when someone authenticates via oAuth, Firebase creates a uid that looks something like google:111413554342829501512, for example.
In Firebase rules, you can do (read and/or write):
".read": "root.child('users').child(auth.uid).child('isAdmin').val() == true"
Is it assumed that I can't read the message by sniffing the network because of the use of HTTPS? Is this how it works - the UID is a shared key used by Firebase rules?
I see that UID in firebase:session::ack in Local Storage in my browser once authenticated.
Knowing someones user id is not a security risk.
For example, I know that your Stack Overflow user id is 4797603. That fact alone allows me to potentially find you on Stack Overflow.
But it does not in any way allow me to pretend that I am Ron Royston. To do the latter I'd need to know the username and password (and any other factor) that you use to sign-in.
The same applies to Firebase. If you know that my uid in some Firebase-backed application is google:105913491982570113897, you cannot suddenly pretend to be me. The Firebase servers verify that the auth.uid value is based on the actual credentials of that user. The only way to do is by signing in as me, which in this case requires you to know my Google credentials.
I advise to use custom ID along side with UID. When your app grows, you do not want to share the UID or pass it around. Also when setting firebase-rules, you'll be referring to UID, which should be kept private.
generate a random string for the ID.
And for sensitive user data, set a rule in firestore, to only allow reading of the document if request.auth.uid == user.uid. This will prevent unwanted access. Read up a bit more on firestore rules, might be relevant for your use case.
i have a serious Question. I am developing the Security Rules for my Firestore Database. So what if someone decompiled my App, stole the GoogleInfo.plist, added this file to his Project, and creates multiple Accounts with it? I mean in the security rules you have to:
allow create: if request.auth != null;
So he could add a new Document every time he adds an FirebaseUser Account.
How to solve and secure this?
Are there other options like sign in with custom field at example:
I create a document ID.
and so we check in the Firestore rules:
match /document/{myDOC}
allow write: if request.auth.code == myDOC;
So what I mean here is, if I can set additional Information to the Request of my App, and check if the additional Information is Equal to the myDOC;
Thanks!!
This is all working by design. There is no "security" information in GoogleInfo.plist. It just contains data that instructs the Firebase SDK on how to find your project and its resources. Without that data, your app would know nothing about your project.
To secure your database, you will need to design your database to allow for per-user security, then write rules that determine which authenticated users can read and write which documents, as suggested in the documentation.
It's not possible to send extra information along with a query for the purpose of security. You should depend on what Firebase auth provides in request.auth in the rules language.
See also: Is it safe to expose Firebase apiKey to the public?
https://firebase.google.com/docs/auth/admin/custom-claims
Firebase Admin SDK allows you to define custom attributes on user accounts.
admin.auth().setCustomUserClaims(uid, {admin: true}).then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});
When you are writing your rules, it is possible to check these custom attributes.
{
"rules": {
"adminContent": {
".read": "auth.token.admin === true",
".write": "auth.token.admin === true",
}
}
}
But as it is stated in the docs, you should consider these points:
Use custom claims to store data for controlling user access only. All other data should be stored separately via the real-time database or other server side storage.
Custom claims are limited in size. Passing a custom claims payload greater than 1000 bytes will throw an error.
It appears that when someone authenticates via oAuth, Firebase creates a uid that looks something like google:111413554342829501512, for example.
In Firebase rules, you can do (read and/or write):
".read": "root.child('users').child(auth.uid).child('isAdmin').val() == true"
Is it assumed that I can't read the message by sniffing the network because of the use of HTTPS? Is this how it works - the UID is a shared key used by Firebase rules?
I see that UID in firebase:session::ack in Local Storage in my browser once authenticated.
Knowing someones user id is not a security risk.
For example, I know that your Stack Overflow user id is 4797603. That fact alone allows me to potentially find you on Stack Overflow.
But it does not in any way allow me to pretend that I am Ron Royston. To do the latter I'd need to know the username and password (and any other factor) that you use to sign-in.
The same applies to Firebase. If you know that my uid in some Firebase-backed application is google:105913491982570113897, you cannot suddenly pretend to be me. The Firebase servers verify that the auth.uid value is based on the actual credentials of that user. The only way to do is by signing in as me, which in this case requires you to know my Google credentials.
I advise to use custom ID along side with UID. When your app grows, you do not want to share the UID or pass it around. Also when setting firebase-rules, you'll be referring to UID, which should be kept private.
generate a random string for the ID.
And for sensitive user data, set a rule in firestore, to only allow reading of the document if request.auth.uid == user.uid. This will prevent unwanted access. Read up a bit more on firestore rules, might be relevant for your use case.
We are using Firebase custom authentication and want to define read/write access rules based on the "auth" payload data. However, for security reasons, we do not want to store the value of auth.uid anywhere in Firebase data. That is, since the user id for example is not part of the data, I cannot use the rule:
auth.uid == newData.child("userId").val()
Is there a way to just pass data to Firebase only for checking in the security rule, but not actually persist the data in Firebase db?
There are three possible sources of data for security rules: Hard coded strings, the database data, or the authentication token (i.e. auth).
You can generate your own authentication tokens, allowing you to specify any information you would like, without storing it in Firebase. Note that the contents of the security token cannot be forged by the user, but they can be read by the user; if your goal is to store some secret info users should not know, this isn't an option.
Note that you can secure any path in the database, so that it is not readable, store data in it, and security rules can still reference that data. Keep in mind that security rules cascade, so you want this data in its own path with no readable parents.
The security token mentioned in your comments sounds completely redundant of the user's auth token. If they have authenticated, you have already verified their uid, so it's unclear why a security token adds any additional value.
I'm new to Firebase and I'm attempting to set-up a simple authentication system using e-mail/password. The initial concept is simple: you register. Then, after logging in, you can access the rest of the mobile app.
In the past, I could set this up with PHP in just a few minutes. But with Firebase, this has become a battle that I can't seem to win.
Using the light documentation found on Firebase's site, I was finally able to successfully register and authenticate a user. Great.
Unfortunately, people can still access the rest of the app whether they are logged in or not. How do I keep the app protected from non-authenticated users?
Also, how do I associated data submitted on a page with an authenticated user?
I've looked at Firebase's documentation. It lacks practical examples for authentication. It keeps referring me to the Firefeed app as a sample. I've looked at Firefeed's code and the authentication system seems 1) excessively complicated for a login system and 2) too intricately tied in to news feeds to be a practical example to learn from.
On the other hand, perhaps I'm just missing something obvious and fundamental. If someone could point me in the right direction, that would be great. Thanks! :-)
(By the way, I tried e-mailing this question to firebase-talk#googlegroups.com, as suggested on Firebase's site... but the group does not appear to exist, according to the bounce-back message from Google.)
Stepping back for a moment, it's worth noting that Firebase Simple Login is an abstraction built on top of Firebase Custom Login for convenience. You can still use your existing authentication with Firebase using Custom Login, if you like.
Firebase Simple Login eliminates the need for you to run a server just for authentication. However, there is no 1-to-1 parallel to the PHP example where the server would govern request access based upon a detected session on the server because all of your logic, templates, etc. lives in client-side code.
In most cases, your client-side logic, templates, assets, etc. will be static and public. What you're really looking to secure is user and application data, and this is where Firebase Authentication (whether using Simple Login or Custom Login) comes in. Firebase Authentication is essentially token generation - taking confirmed, identifiable user data and passing it securely to Firebase so that it cannot be spoofed.
Read / write access to different paths in your Firebase data tree is governed by Firebase Security Rules, which allow you to write JavaScript-like expressions to control which clients can access which data.
Here's an example:
Suppose you have a user list, where each user is keyed by user id,
such as /users/<user-id>/<data>, and you want to ensure that only
the logged in user can read / write their own data. With Simple Login,
this is really easy!
Looking at the After
Authenticating
section of Email / Password authentication docs, we see that the
auth variable in our security rules will contain a number of fields
after authenticating, including id, the user's unique user id. Now
we can write our security rules:
{
"rules": {
".read": false,
".write": false,
"users": {
"$userid": {
".read": "auth != null && auth.uid == $userid",
".write": "auth != null && auth.uid == $userid"
}
}
}
}
What's going on here? Firebase Authentication (using Simple Login)
securely generated a token containing your verified user data upon
login, and that token data becomes available in your security rules
via the auth variable for the connection. Now, in order for a client
connection to read or write to /users/xyz, the user must be
authenticated and authenticated as user xyz.
Most of the above is covered in the Security Quickstart but it is admittedly a little hard to wrap your head around.
Back to your initial question, if you want to redirect away from certain paths when a user is not authenticated, you can do the following:
var ref = new Firebase(...);
var auth = new FirebaseSimpleLogin(ref, function(error, user) {
if (!user) {
// we're logged out, so redirect to somewhere else
} else {
// we're logged in! proceed as normal
}
});
Hope that helps!
Please note:
Login is now a core feature of Firebase. Simple Login has been
deprecated and documentation for this client is now available on
Github.
See this page for more info:
https://www.firebase.com/docs/web/guide/user-auth.html