I would like to create a newsletter subscription form. Meaning a user can register to my newsletter without creating an account.
I would like to do a simple insertion. But what is the best way to secure it ?
I have implemented "Anonymous Log In" (https://firebase.google.com/docs/auth/web/anonymous-auth)
I need basic rules, read to check is the user is allready registered and write to push user data into Realtime DB.
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
when the Anonymous Login is done, I push into the Realtime Database.
let dbConnection = firebase.database().ref('/newsletter_user');
dbConnection.push(datas).then(callback);
The problem is that Anonymous Auth do not check Auth Domain. (How to block localhost in firebase?)
Meaning a user can grab my ApiKey and perform insert or read data into my realbase database.
Then it do not seems really secure to me. What can I do to improve security ?
One thing you can do to improve your security is limiting your database access. Currently you have basic read/write rules for your database wich means that someone that is authenticated can do anything in your database. If I want I could write an entire encyclopedia in your datadump node :)
So the first thing you can do is limit read/write access to specific locations:
{
"rules": {
"newsletter_user" : {
".read": "auth != null",
".write": "auth != null"
}
}
}
Now I can only do something under the newsletter_user node.
Next you can use validation rules to make sure only the data you want can be written:
{
"rules": {
"newsletter_user" : {
".read": "auth != null",
".write": "auth != null",
// a valid newsletter_user must have attributes "color" and "size"
// allows deleting newsletter_user (since .validate is not applied to delete rules)
".validate": "newData.hasChildren(['color', 'size'])",
"size": {
// the value of "size" must be a number between 0 and 99
".validate": "newData.isNumber() &&
newData.val() >= 0 &&
newData.val() <= 99"
},
"color": {
// the value of "color" must exist as a key in our mythical
// /valid_colors/ index
".validate": "root.child('valid_colors/' + newData.val()).exists()"
}
}
}
}
Related
I have been getting email notifications about my Firebase rules not being secure even though I set the data to be readable only by authenticated users and I set the write to be false. My application basically shows currency exchange rates and I create an anonymous user once the app loads for the user to be able to read the data so I think it makes sense to have such rules? Here are my rules:
{
"rules": {
".read": "auth.uid != null",
".write": false
}
}
Update: I updated my rules to:
{
"rules": {
"currencies":{
".read": "auth.uid != null",
},
".write": false
}
}
And here is my data:
(https://i.stack.imgur.com/in1BV.png)
I set the data to be readable only by authenticated users
While that is a good starting point, it means I can get all your (user's) data by signing in and reading the root. This is not secure and open to abuse, which is why you receive alerts about it.
You should follow the principle of least privilege, and only allow in your rules exactly what your code does.
In the unlikely event that your code really reads the root, you can disable the alert in the Firebase console.
In all other cases, you'll want to reduce the read permission to exactly how your code reads it. For example, if your code reads from two top-level nodes, you can secure that with:
{
"rules": {
"users": {
".read": "auth.uid != null",
},
"messages": {
".read": "auth.uid != null",
},
".write": false
}
}
In a scenario like this, you're likely to read the /messages first, and then read the profile information for specific users mentioned in those messages. You can better secure that like this:
{
"rules": {
"users": {
"$uid": {
".read": "auth.uid != null",
}
},
"messages": {
".read": "auth.uid != null",
},
".write": false
}
}
Now a malicious user can't read all of /users in one go, but can read the profile only once they know the UID. A sufficiently motivated user can still read your entire database, but it'll take a lot more effort - which discourages abuse.
Also think of whether the user needs to read all message, or (say) maybe just the 50 most recent ones. The latter use-case you can secure better with query based security rules:
"messages": {
".read": "auth.uid != null &&
query.orderByChild == 'timestamp' && &&
query.limitToLast <= 50"
},
Now the user can only read the 50 most recent messages, and thus (with the previous) also only the user profiles that participated in those messages.
Finishing off with the golden rule: only allow in your rules exactly what your code does. Make sure your code works, but any different API calls get rejected.
I am trying to block hackers from modifying or seeing the data on my Firebase database. Users in my database contains the user IDs of all the users in Authentication tab. Can I make the database to only let see data by the user logged in?
This is my database structure:
firebase database structure
I want to allow users to modify their own user ID's entry in "users" and I want to allow only the admin to control "orders". Im using Firebase in a Electron app so client is connecting to firebase in Node.js
Yes, this is definitely doable using Firebase Database Rules
What you've described seems like it would have three parts, but they we they combine also affects how you need to write them.
I'll start by going over the 3 pieces separately and then will move into combining them.
Read/Write By Only Logged In Users
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
By placing this at the top level of your rules, you will prevent any reading or writing by anyone not logged in through one of the supported Firebase authentication methods.
Users Modifying User Entry
{
"rules": {
"users": {
"$uid": {
".write": "auth.uid === $uid"
}
}
}
}
This specifies that under the user path, for each user id (using the $uid syntax to specify it as a variable to be used later), only allow writing when the current authenticated user id matches that same id.
Admin Control Of Orders
{
"rules": {
"orders": {
".read": "auth != null && auth.token.isAdmin",
".write": "auth != null && auth.token.isAdmin"
}
}
}
We take a similar approach for orders as we did for users entries, but instead of checking the user id we simply say that only an admin can do reads and writes anywhere under orders.
Putting It All Together
Now, if these rules came together in a way such that more specific rules overrode less specific rules (like CSS selectors, for instance), then we could simply merge all of the above together like this:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"orders": {
".read": "auth != null && auth.token.isAdmin",
".write": "auth != null && auth.token.isAdmin"
},
"users": {
"$uid": {
".write": "auth.uid === $uid"
}
}
}
}
However, Firebase database rules are somewhat of an inverse of this. Providing access in shallower rules will override any deeper rules attempting to deny access (although it is possible to grant more privileges in deeper rules). So the above will actually give full read/write access to the entire DB by any logged in user.
In light of that, we will need to be more careful when combining these, to ensure that our separate intents stay valid. Most importantly, the first section Read/Write By Only Logged In Users will be the section that requires modification, since it was at the top level.
Luckily, our rules from parts 2 & 3 take care of most of what we were getting from part 1 anyways. This only holds true if there are only orders and users in your db. The only thing we are missing is a read rule on users. It's not entirely clear what sort of rules you want for reading user data, since you only specify that a user can only write his or her own data, so I will assume that all logged in users can read user data.
The merge then becomes:
{
"rules": {
"orders": {
".read": "auth != null && auth.token.isAdmin",
".write": "auth != null && auth.token.isAdmin"
},
"users": {
".read": "auth != null",
"$uid": {
".write": "auth.uid === $uid"
}
}
}
}
Please note that you will need to duplicate the auth != null rule for read and/or write in other nodes of your db if you have them, since here you only show off orders and users.
Hope that helps!
I want to stop the signup process if the username exists. I don't want to create Email\Password (Authentication) and other info if the username exists
database rules:
{
"rules": {
"users": {
".read": "(auth != null)",
".indexOn": ["username"],
"$uid": {
"$username":{
".write": "(auth != null) && !data.exists() || newData.exists()"
}
}
}
}
}
I have added !data.exists() in .write , but still it duplicate usernames.
And then I added ".validate" to make username atleast 3 characters, to see if the signup process gonna fail if its longer, but it created email\password and other stuff but didn't wrote any data in database root\users\
After registered new user with email\password I got this error, Which means username couldn't be more than 3 characters. How to stop the signup process and show alert message at this situation?
[Firebase/Database][I-RDB03812] setValue: or removeValue: at /users/jt94ARqOoodtU3kYp3W1MtlUZ4m1 failed: permission_denied
Please help me if you could fix the problem.
Your simplified rules are:
{
"rules": {
".write": "(auth != null)",
"users": {
"$uid": {
"username": {
".write": "auth != null && (!data.exists() || !newData.exists())",
So you first grant any authenticated user write access on the root of the data and subsequently say that a user can only write if there is not data a the current location.
That won't work: once you grant permissions at a certain level, you cannot take that permission away on a lower level.
The Firebase documentation describes this as Read and Write rules cascade:
Child rules can only grant additional privileges to what parent nodes have already declared. They cannot revoke a read or write privilege.
My app allows anyone to write a new message (the message cannot be updated or deleted), which should then only be visible to authenticated users. The authenticated users can edit the data (which will include things like flag as important, etc.). Think of the app like a private suggestion box (anyone can submit a suggestion but only the admins can view the submitted suggestions). I'm using the Firebase simulator and the following fails but it shouldn't:
Firebase Simulator
Write
Location: messages/
Authenticated: false
Data (JSON)
{
"key": "value"
}
Firebase Database Rules
{
"rules": {
"messages": {
"$message": {
".read": "auth !== null",
".write": "!data.exists() || auth !== null",
},
},
"users": {
".read": "auth !== null",
".write": "auth !== null"
}
}
}
I think that's because you're testing with messages/ whereas only writes to messages/{message-id} would be allowed. Try writing to messages/somethingrandom.
I'm working on a Firebase rule configuration to control read/write access to my database. I had more rules written originally, but I've pared things down during troubleshooting. Here is my current rule configuration:
{
"rules": {
"developers": {
"$dev": {
".write": "!data.exists() && auth != null",
".read": "auth.devBucket === $dev",
"$proj": {
".read": "auth.devBucket === $proj",
"shared": {
".write": "!data.exists() || (auth.devBucket === $dev && auth.projBucket === $proj)"
}
}
}
}
}
}
What I'm trying to do is allow users of the Firebase to create a $dev node, $proj node, and shared node as long as they don't already exist and the user is authenticated. Then, I want to allow a user to have free write access within the shared node as long as their auth token's devBucket matches the $dev node they're writing within and their auth token's projBucket matches the $proj node they're writing within. I'm using the Firebase custom auth system for Android and I've loaded my tokens with these devBucket and projBucket variables. Authentication is definitely working according the my logcat, but I'm definitely getting permission denied errors with my current rules. I've been pouring over the Firebase Rule documentation and questions here for days and I am still puzzled as to the nuances of how their rule system works.
According to the documentation rules carry through to lower levels of nesting in the JSON, I'm just having trouble understanding how I can write a rules that allows a node and it's children to be created once, but also allows any number of children to be written or overwritten under shared if you're properly authenticated.
Does anyone have any idea how I could write rules to accomplish what I'm trying to do?
EDIT: I think it's also worth mentioning that I'm getting permission denied errors when I try to point listeners to my nodes too.
I figured out a configuration that worked for me.
{
"rules": {
"developers": {
".write": "!data.exists() || auth != null",
".read": "auth != null",
"$dev": {
".write": "!data.exists() || (auth != null && auth.devBucket == $dev)",
".read": "auth != null && auth.devBucket == $dev",
"$proj": {
".write": "!data.exists() || (auth != null && auth.projBucket == $proj)",
".read": "auth != null && auth.projBucket == $proj"
}
}
}
}
}