I am currently trying to implement rate limting by checking the timestamp of the last post and then add + 60sec to it and then check if it is smaller(<) then the current Firebase Server Timestamp(now).
It somehow always returns true and grants access ?!
These are my Rules:
{
"rules": {
"posts": {
".read": true,
".write":
"(root.child('users').child(auth.uid).child('lastPost').val() + 60) < now"
}
}
}
This is my database structure
{
"posts": {
"-KV70ppGGTEtXY4_Q4UC": {
"author": "abcdedef-uid-ojifgoöifjgssgd",
"description": "Thats the post description",
"title": "Thats the post title"
}
},
"users": {
"2uy7323nTodMHcVxeEDJzoexH302": {
"canPost": true,
"email": "cryptic.pug#firebase.com",
"lastPost": 14776667681,
"profile_picture": "https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg",
"username": "Cryptic Pug"
}
}
}
Thanks for your Clue Vladimir!
As I haven't found this kind of soulution anyware I would like to share the answer here officially:
{
"rules": {
"posts": {
".read": true,
".write":
"(root.child('users').child(auth.uid).child('lastPost').val() + 60000) < now"
}
}
}
Explanation:
When a user posts something you always update the Value in the Database with the Value of firebase.database.ServerValue.TIMESTAMP to the user information.
In the Rule language you read the Timestamp of the Last Post is read out of the user who wants to post (auth.uid in FB Rule Language) and add 60 seconds (*1000 as Firebase uses Milliseconds in it's timestamp), which would be the time when the user would be allowed to post again. And Then check if the current server timestamp is higher (<) than the time the user is allowed to post again.
Hope It helped you guys, Happy Coding - Doing Firebase for 3 days and it's great!
Related
I have a public website that I share with a few of my colleagues and I am using firebase hosting and a real-time database to store my data. Below you will see my security rules. Firebase keeps sending me warning emails that my database has public access. I contacted their support and they recommended that I go further through the documentation and avoid root access to my data nodes. I did as recommended, but I am still getting the warning emails with the same message for insecure
rules.
My desired behavior is to have the Posts node be read publicly
Thank you in advance for your time and help.
{
"rules":
{
".read": false,
".write" :false,
"data":
{
"Posts":
{
".read":true,
".write":false
}
},
"users":
{
"data":
{
"Posts":
{
".read":"auth != null",
".write":"auth != null"
}
}
}
}
}
I am coding a microblogging site on Firebase, to which I am very new. I am storing user information (e.g. introduction, profile pictures) and posts the users write like below structure:
{
"posts" : {
"postid" : {
"category": "xx",
"content": "xx",
"uid":"xx"
}
},
"users" : {
"uid" : {
"intro" : "xx",
"nickname" : "xx",
"profile_picture" : "xx"
}
}
}
I'd like make following rules:
Any signed-in users will be able to post, but only will be able to edit their own posts afterwards
For user info, they will only be able to post/edit their own.
I set the database security rules like below.
"users": {
".read": true,
"$uid": {
".write": "auth.uid===$uid"
},
},
"posts": {
".read": true,
".write": "auth!==null",
},
Here are a couple of issues:
a. This rule will enable any bad user to edit any other post that is not his/her own.
b. When trying to write to users node, there is no existing UID child node at the moment and the users are denied permission to write.
How can I change the database rules to solve the two issues? My preference is doing so without rewriting the front-end code or change the database structure...would love to seek some advice here!
To only allow users to write their own posts (or create new posts with their own UID), you can check the value of the uid field in write operations:
"posts": {
".read": true,
".write": "auth !== null && auth.uid === newData.child('uid').val",
},
I want to implement a rule to read data for the last say 5 or 10 minutes. My database is like this:
.
I want to allow user to read messages of last 10 minutes but the rule is not working and i am not able to make it work I tried different rules like this.
"Messages": {
"$messagekey": {
"$key": {
".read":"root.child('Users/Messages/'+$messagekey+'/'+$key+'/messageTime').val()> (now - 600000000)", }
},
".write":"auth!=null"
}
And this as well
"Messages": {
"$messagekey": {
"$key": {
".read": "data.child('messageTime').val() > (now - 600000)"
}
},
".write": "auth!=null"
}
Any help or suggestions please.
Error message
Edit 2
Using this rule I am still unable to make it work
{
"rules": {
"Messages": {
"$messagekey": {
"$key": {
".read": "data.child('messageTime').val() > (now - 90000000)"
}
},
".write": "auth!=null"
}
}
}
and here is my data
Users
Messages
WZTDdZslJrMFqgDLNWo4jXehsF02_nUVrJthSqHXH8Ur0stRz2tihxdg1
-LPFPTBx7BA6urp-CSbZ
messageText: "Hello"
messageTime: 1540059028779
messageUser: "Joker's Grin"
messageUserId: "WZTDdZslJrMFqgDLNWo4jXehsF02"
-LPFPUB5efDAIVu_AxP0
messageText: "how are you"
messageTime: 1540059028779
messageUser: "Crazy Eights"
messageUserId:"nUVrJthSqHXH8Ur0stRz2tihxdg1"
I am using two wildcards "messagekey" and "key" to reach messageId. Also tried these rules as well none of these work except read: true.
".read": "data.child('messageTime').val() >0"
".read": "data.child('messageTime').val() !==null"
".read": "data.exists()"
".read": "data.hasChild('MessageId')"
Here are the screenshots
I just added this value to a database of my own:
"52903949": {
"messageTime": 1540054472392
}
Where 52903949 is the ID of your question, and 1540054472392 is the value returned by Date.now() when I added it.
Then I added these rules:
{
"rules": {
"52903949": {
".read": "data.child('messageTime').val() > (now - 600000)"
}
}
}
And was able to access the data in the simulator with this result:
If I make the 600000 shorter, the read fails since that time has already expired.
Updated screenshot with nested children:
So I've set up a simple db/web form to collect some user data. Rn I am trying to figure out the rules thing but I am running into this problem - if my read flag is set to true then I can simply run this in the console
var ref = firebase.database().ref();
ref.on("value", function(snapshot) {
console.log(snapshot.val());
}, function (error) {
console.log("Error: " + error.code);
});
and expose the users which should not be the possibility. If I set read to false then I cant access the DB upfront to validate if email address is unique or not. I guess I need to achieve 2 things:
Prevent db snooping through the dev tools running any snippets
Make sure email address is unique.
p.s. My currents rules (prevent delete, prevent read, make sure POST request has certain fields):
{
"rules": {
"users": {
".read": false,
"$uid": {
".write": "!data.exists()",
".validate": "newData.hasChildren(['first_name', 'last_name', 'email', 'country', 'amount'])"
}
}
}
}
To avoid duplicates you will want a validation check in the DB rather than reading data from the client and checking (you can't trust the client).
Since there is no easy way to check for duplicate child values in a firebase base collection, you will need a separate collection to track emails and then validate your emails against that, i.e.:
{
"rules": {
"users": {
".read": false,
"$uid": {
".write": "!data.exists()",
".validate": "newData.hasChildren(['first_name', 'last_name', 'email', 'country', 'amount'])",
"email": {
".validate": "!root.child('emails').child(newData.val()).exists()"
}
}
},
"emails": {
".read": false,
".write": "!data.exists()"
}
}
}
You will then need to write the users' emails to the email collection as the users are added, e.g.:
var ref = firebase.database().ref('emails/'+email);
ref.set(uid)
I am struggling to design a simple data sharing app using firebase. To illustrate, consider a messaging app similar to facebook:
Users can add friends - the request must be accepted
Users can add posts
Users can view all posts submitted by themselves and friends
I can set up the security rules for this, but filtering or denormalizing data seems overly cumbersome and inefficient. A sample data model might look like:
{
"users": {
"user1": {
"friends": {
"user2": { "accepted": true }
}
},
"user2": {
"friends": {
"user1": { "accepted": true }
}
},
"user3": {
"friends": {}
}
},
"posts": {
"generatedId1": {
"message": "blah blah",
"user_id": "user1"
},
"generatedId2": {
"message": "lorem ipsum",
"user_id": "user2"
},
"generatedId3": {
"message": "hello world",
"user_id": "user3"
}
}
}
Security rules to achieve the above requirements would look something like:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
"posts": {
"$post": {
".read": "root.child('users').child(data.child('user_id').val()).child('friends').child(auth.uid).child('accepted').val() === true",
".write": "newData.child('user_id').val() === auth.uid"
}
}
}
}
It's probably not quite right, but close enough to illustrate. We can see users can only read and write their own user record, can only add posts with their own user id and can read posts where there is a corresponding accepted friend record.
So my question essentially boils down to how to execute a query that will only return the posts a user is allowed to see? Since security rules are not filters, they do not help us in this case.
My understanding so far is that the posts should be denormalized, basically duplicate the post for each user. This is horribly inefficient - if the user has 100 friends, this means duplicating the data 100 times. Retrospective updates (i.e. adding a new friend or removing an existing one) are also complex to manage.
Another option is to retrieve a users own list of friends and retrieve the posts for those users in separate queries, but again, if a user has 100 friends, this would mean executing 100 separate "queries".
Essentially, I'm attempting to implement a many to many relationship. What is the guidance / best practice here?