Search code examples
firebasegoogle-cloud-firestorefirebase-authenticationfirebase-security

firebase security rule to allow admin users to read/write all other users creates a security breach?


I am using cloud Firestore database and I have 'users' collection on my root database and in it there are all the users documents (named as the uid) and inside it the data I collect. I wanted to allow users to only read/write to their own collections so I started my security rules with

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow only authenticated content owners access
    match /users/{userId}/{documents=**} {
      allow read, write: if request.auth.uid == userId
    }
  }
}

then I realized that the users don't have permission to create their own main document (it created it as a 'ghost' document). I also want to add data to it such as display name and email, so I added

// Allow users to read/write their own main user object
match /users/{userId} {
  allow read, write: if request.auth.uid == userId
}

all works well but now I want to allow admin users to read/write to all user's collections and documents. firebase's documentations suggests adding a key-value to the user's document such as (admin:true), so editing my security rules like so should do the trick:

match /users/{userId}/{documents=**} {
  allow read, write: if request.auth.uid == userId
  allow read, write: if get(/users/$(request.auth.uid)).data.admin == true; //-security breach?
}

but doesn't it creates a security breach where a user can technically update his own document with (admin:true) and gain admin privilege? if so, i'm guessing my approach should be creating a separate admins collection and add my admins there and then do something in the lines of

allow read, write: if get(/admins/$(request.auth.uid)) != null

? or is there something prettier I can use?


Solution

  • This rule is indeed going to allow anyone to mark themselves as an Admin, defeating the whole purpose of your rules (don't do this):

    allow read, write: if get(/users/$(request.auth.uid)).data.admin == true;
    

    What you rather want to do is:

    • Either give the admin users a custom admin: true claim, which you can then check in your rules with: allow read, write: if auth.token.admin === true;
    • Keep an explicit list of UIDs for your admins in your security rules, and then check that in each rule. So: allow read, write: if isAdmin(); and then function isAdmin(request) { return request.auth.uid == "KqEizsqUQ6XMkUgLUpNxFnzLm4F3" }.
    • Keep a list of admin UIDs in another (not normally writeable) collection in Firestore, and check against that. That's pretty much what you've described, and is the most common approach if you want to be add/remove admins quickly and easily.