I'm clearly missing some fundamental aspect of firebase security, because this shouldn't work. I would expect it to throw a validation error when attempting to push invalid data. (Inserting a new node into /nodes)
Rules:
{
"rules": {
"nodes": {
".read": "auth !== null && auth.provider === 'google'",
".write": "auth !== null && auth.provider === 'google'",
"user": {
".validate": "newData.val() === auth.uid"
},
"ts": {
".validate": "newData.val() <= now && newData.val() >= (now-1000*60*60*24)"
}
}
}
}
Then in my console I try to intentionally insert invalid data:
ref.child('nodes').push({
'user': 'abc',
'ts': 123
}, function(err){console.log(err);});
Which logs null, and when I check my database it was inserted, no validation errors! I know I've got something fundamentally wrong, because a validation rule right after the .read and .write rows of the following disallows any writing. .validate": "newData.hasChildren(['user', 'ts'])",
{
"nodes" : {
"-KAgH0BLneWfGu8NymBo" : {
"ts" : 123,
"user" : "abc"
}
}
}
Whoops. Missing "$node_id"
{
"rules": {
"nodes": {
"$node_id":{
".read": "auth !== null && auth.provider === 'google'",
".write": "auth !== null && auth.provider === 'google'",
"user": {
".validate": "newData.val() === auth.uid"
},
"ts": {
".validate": "newData.val() <= now && newData.val() >= (now-1000*60*60*24)"
}
}
}
}
}
Related
I have defined a realtime database rule as follows:
{
"rules": {
".read": false,
".write": false,
"devices": {
".read": "auth.uid != null && query.orderByChild == 'ownerUid' && query.equalTo == auth.uid",
"$device": {
".read": "data.child('ownerUid').val() == auth.uid",
"nickname": {
".write": "data.parent().child('ownerUid').val() == auth.uid",
".validate": "newData.isString() && newData.val().length < 30"
},
"ownerUid": {
".validate": "root.hasChild('users/' + newData.val())"
},
... additional fields here
}
}
}
}
In my web application, using reactfire and firebase npm modules, I have queried for a device as follows:
const devicesRef = useDatabase()
.ref(`devices`)
.orderByChild('ownerUid')
.equalTo(user.uid);
const { data: devices, status } = useDatabaseListData<Device>(devicesRef, { idField: 'id' });
This appears to work, but if I look in the network tab, I can see all of the data come back, not just the data that is supposed to come back. The data returned to my code is the data that I would expect.
Note in the screenshot below that all data comes back, even data that does not have ownerUid defined.
I am using the example from the documentation almost exactly: https://firebase.google.com/docs/database/security/rules-conditions#query-based_rules
Am I doing something wrong? or is this a bug in Firebase?
I discovered the solution after upgrading my firebase client version and getting some new errors from it. It turns out the issue was that I was missing an index on ownerUid.
The new rules look like this:
{
"rules": {
".read": false,
".write": false,
"devices": {
".indexOn": ["ownerUid"],
".read": "auth.uid != null && query.orderByChild == 'ownerUid' && query.equalTo == auth.uid",
"$device": {
".read": "data.child('ownerUid').val() == auth.uid",
"nickname": {
".write": "data.parent().child('ownerUid').val() == auth.uid",
".validate": "newData.isString() && newData.val().length < 30"
},
"ownerUid": {
".validate": "root.hasChild('users/' + newData.val())"
},
... additional fields here
}
}
}
}
I was writing security rules in my database but I do not understand why the validation does not pass it ..
I just want people to be able to type in "Extra" if the key says "nombre".
In case it's not the key "nombre", don't let it.
the rules:
"rules": {
"Users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid && root.child('Users').child(auth.uid).exists() === false || newData.hasChild('Extra')",
".validate": "newData.hasChildren(['nombre'])",
"Extra":{
}
}
}
}
Is the writing rule wrong? Does someone explain to me why?
TEST1 wrong
TEST2
You're writing to location /Users/$uid/Extra, so the nombre property ends up in /Users/$uid/Extra/nombre. To test the rule, you'll want to write to /Users/$uid.
If instead you want to allow the JSON like this:
Users: {
myUserId: {
Extra: {
nombre: "MT Designer"
}
}
}
Then your rules currently don't work, because you're validating that nombre exissts under myUserId. It should be:
{
"rules": {
"Users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid && root.child('Users').child(auth.uid).exists() === false || newData.hasChild('Extra')",
"Extra":{
".validate": "newData.hasChildren(['nombre'])",
}
}
}
}
}
Update: 2020-07-10
From the new screenshots it seems that yyou haven't applied the change from above yet, so I'd first recommend doing that. But if you want to reject other child nodes in Extra, you can do that by changing the rules to:
"Extra":{
"nombre": {
".validate": "data.isString()"
},
"$other": {
".validate": false
},
}
So this validates that the name is a string, and rejects all other child nodes.
From comments, apparently this is what OP ended up with:
"Users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid && root.child('Users').child(auth.uid).exists() === false || newData.hasChild('Extra')",
"$othernode": {
".validate": false
},
"Extra": {
"nombre": {
".validate": "newData.isString()"
},
"$other": {
".validate": false
},
}
}
},
I've been a while working with firebase and I love it but today I'm working on security rules and I'm getting an error with simulator, my code looks as below:
{
"rules": {
"users":{
"$uid":{
".read": "auth.uid != null",
".write": "auth.uid != null",
".validate":"newData.child('profile').child('userName').isString()&& newData.val().length < 15"
}
}
}
}
The error appear just when i add the lenght validation. When I do:
{
"rules": {
"users":{
"$uid":{
".read": "auth.uid != null",
".write": "auth.uid != null",
".validate":"newData.child('profile').child('userName').isString()"
}
}
}
}
Works fine, any idea why this is happening, I have readed the documentation on: https://firebase.google.com/docs/database/security/securing-data and many other examples and I just can't find the error. Thank you su much in advice and happy coding.
You can add validation like this to your field as per this example.
{
"rules": {
"users": {
"$user_id": {
// grants write access to the owner of this user account
// whose uid must exactly match the key ($user_id)
".write": "$user_id === auth.uid",
".read" : "$user_id === auth.uid",
"familyName" : ".validate": "newData.isString() && newData.val().length > 1 && newData.val().length < 100",
"givenName" : ".validate": "newData.isString() && newData.val().length > 1 && newData.val().length < 100",
"age" : ".validate": "newData.isNumber() && newData.val() > 13 && newData.val() < 110",
"email": {
// an email is only allowed in the profile if it matches
// the auth token's email account (for Google or password auth)
".validate": "newData.val() === auth.email"
}
}
}
}
}
Ok I have solved the correct syntax:
{
"rules": {
"users":{
"$uid":{
".read": "auth.uid != null",
".write": "auth.uid != null",
".validate":"newData.child('profile').child('userName').isString()&& newData.val().length < 15"
}
}
}
}
{
"rules": {
"interviews": {
".read": true,
".write": "auth != null && root.child('admins').child(auth.uid).exists()",
"thumbnail" :{
".validate" : "newData.val() != 0 || (newData.val() == 0 && data.val() == 'uploading')"
},
"soundbyte" :{
".validate" : "newData.val() != 0 || (newData.val() == 0 && data.val() == 'uploading')"
}
},
"token": {
".read": false,
".write": "auth != null && auth.isAdmin == true"
},
"admins":{
".read" : false,
".write": "auth != null && auth.isAdmin == true"
}
}
}
Can you tell any problem with my security rules, as I am trying to add a validation to my thumbnail's data.
Here is a little data structure example I am gonna use with it.
interviews
-K_l_pkOTUYovqRwajln
detail: "YO"
soundbyte: "interview_sb_KlpkOTUYovqRwajln.mp3"
thumbnail: "uploading"
title: "Episode Five"
video_url: ""
-K_ll31srQ46vgtDXX1n
detail: "Duncan lives in a town."
soundbyte: "interview_sb_KllsrQvgtDXXn.mp3"
thumbnail: "interview_thumb_KllsrQvgtDXXn.jpg"
title: "Duncan"
video_url: ""
I want to allow all logged in user access to create new content, but only those who are owners should be able to update the data. I can't figure out how to do this, this is what I have tried:
{
"rules": {
".read": false,
".write": false,
"videos": {
".read": true,
".indexOn": ["id", "title_lower_case"],
"$video": {
".write": "(auth !== null && auth.provider === 'password' && (!newData.exists() || newData.hasChildren())) || (auth.uid === root.child('videos').child($video).child('uid').val())",
".validate": "newData.hasChildren(['id', 'title', 'title_lower_case', 'uid'])",
"id": {
".validate": "newData.isString() && newData.val().length >= 5 && newData.val().length <= 1000"
},
"title": {
".validate": "newData.isString() && newData.val().length >= 2 && newData.val().length <= 1000"
},
"title_lower_case": {
".validate": "newData.isString() && newData.val().length >= 2 && newData.val().length <= 1000"
},
"uid": {
".validate": "newData.val() === auth.uid"
},
"$other": {
".validate": false
}
}
},
"users": {
".read": true,
"$user": {
".read": "auth !== null && auth.provider === 'password'",
".write": "auth.uid === $user && (!newData.exists() || newData.hasChildren())",
".indexOn": "name_lower_case",
".validate": "newData.hasChildren(['email', 'name', 'name_lower_case'])",
"email": {
".validate": "newData.isString() && newData.val().length <= 2000"
},
"name": {
".validate": "newData.isString() && newData.val().length >= 2 && newData.val().length <= 2000"
},
"name_lower_case": {
".validate": "newData.isString() && newData.val().length >= 2 && newData.val().length <= 2000"
},
"$other": {
".validate": false
}
}
}
}
}
I thought that this part allowed any logged in user to create a new node:
auth !== null && auth.provider === 'password' && (!newData.exists() || newData.hasChildren())
And this part only allowed owners to edit the node:
auth.uid === root.child('videos').child($video).child('uid').val()
Btw, I use the simple login feature in firebase if that has anything to say.
Doing the below seemed to do the trick. If the auth variable exists, then allow write abilities. Due to me not fully understanding these rules, I have to wonder if this is running against the current user or if it's checking to see if any user is logged in.
{
"rules": {
"product":{
".read": true,
".write":"auth !== null"
}
}
}