1
votes

I'm creating an android app using firebase. My database structure is like this,

user:{
     $uid:{
           uname: abc,
           age: 20,
           email: [email protected],
           premium: true,
           score: 80
           }
       }

And security rules look like this,

  {
   "rules": {
           "user":{

                 "$uid":{
                        "score":{
                              ".read":"auth.uid===$uid",
                              ".write":"auth.uid===$uid"
                               },
                        "$others":{
                                ".read":"auth!=null",
                                ".write":"auth!=null"
                                }
                       }
                   }
             }
    }

And this is the code I read data from firebase,

DatabaseReference myRef = FirebaseDatabase.getInstance().getReference()
                               .child("user").child(uid);

    myRef.addListenerForSingleValueEvent(new ValueEventListener() {
          @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
           users = dataSnapshot.getValue(User.class);
            //set values to associated textviews
        }

         @Override
       public void onCancelled(DatabaseError databaseError) {}

  });

The problem is ".read" rule only in simulator, although ".write" rule work in both app and simulator. When I change the rule to this,

  {
   "rules": {
           "user":{
                 "$uid":{
                          ".read":"auth!=null",
                          ".write":"auth!=null",
                        "score":{
                              ".read":"auth.uid===$uid",
                              ".write":"auth.uid===$uid"
                               },
                        "$others":{
                                ".read":"auth!=null",
                                ".write":"auth!=null"
                                }
                       }
                   }
             }
    }

Its work for both read and write.But this is not secure. Why can't I read data even auth.uid = myUid? However ".write" rule always goes fine.

1

1 Answers

0
votes

In your first solution nobody can read /users/$uid, so it makes sense your listeners gets rejected.

To make it work, you'll need to grant the user who can read all data access to /users/$uid. As far as I can see:

  1. A user can read their own entire node.
  2. All authenticated users can read the all but the score of a user.

The closest you can get to that is:

{
    "rules": {
        "user": {
            "$uid": {
                ".read": "auth.uid===$uid",
                ".write": "auth.uid===$uid"
                "score": {
                },
                "$others": {
                    ".read": "auth!=null",
                    ".write": "auth!=null"
                }
            }
        }
    }
}

With these rules:

  1. Each user can read their entire node.
  2. All authenticate users can read each specific property of each other user, but not their score.

So each authentication user can read /users/anyUid/foo and /users/anyUid/bar, but not /users/anyUid/score.


The tricky bit here is that users can still not read /users/anyUid of any user. The reason for this is that permission cascades: once you can read a node, you can read all data under that node. So once you grant anyone read permission on /users/$uid, you can't remove that permission at a lower level.

For this reason you'll typically want to store data that requires separate access permission into separate top-level nodes. If we rewrite your rules a bit, we get to:

  • All authenticated users can read all user profiles.
  • An authenticated user can only read/write their own score.

That'd translate into this data structure:

profiles: {
  uid1: { ... },
  uid2: { ... }
},
scores: {
  uid1: ...,
  uid2: ...
}

And now you can easily secure it with:

{
    "rules": {
        "profiles": {
            "$uid": {
                ".read": "auth!=null",
                ".write": "auth!=null"
            }
        },
        "score": {
            "$uid": {
                ".read": "auth.uid===$uid",
                ".write": "auth.uid===$uid"
            }
        }
    }
}