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.