frankish
frankish

Reputation: 6826

Securing Firebase Realtime Database while keeping optimisations

How can one protect the structure if the input data is a json object composed of multiple json objects with un-predefinable unique keys per parent node (per user for example)?

The same question with an example:

So, assuming a user has marked 3 foods with id of E5435, 59z195J, k311 as favorites: The following structure seems to be OK.

{ fav: { $uid { "E5435":1, "59z195J":1, "k311":1 } } }

But! How one would prevent the structure from expanding with arbitrary data? You could add validation rules to check data type and length etc.. but child names are undefined and can be anything like the above example.

I do not want to keep 1 long favorites string as that means sync won't work partially and I must overwrite the data and download every single time.

I cannot add all possible food IDs as it will make the structure very long and that list may grow any time.

How can one add a rule to secure this structure when possible input is something like this:

/fav/UID1234567/ <-- {"AAAA5":1,"G545345":1,"D454WW":1}

(Notes: Food IDs are intentionally written with mixed up characters. If that makes it easier to analyse the question, they can be replaced with names.

AAAA5 --> APPLE, D454WW --> RICE ) forming: /fav/UID1234567/ <-- {"APPLE":1,"RICE":1}

And keep in mind that there cannot be predefined food list to select from: So, nothing like [BANANA, APPLE, RICE, SUSHI,.....] exists. That HUGE list exists but outside of Firebase.

When the client downloads content of /fav/USERID/ fully (only 1 time), it will compare the whole list against some other list to filter those items and show them as favorites.

End of notes)

Additional info: To be clear, how would you prevent the usage of some long text instead of "APPLE" and again a long text instead of it's value '1'. As these data will increase firebase costs and can be considered as attacks. Usual validation rules do not seem to work as in this example we are not pushing values 1 by 1, but multiple of them. So, there is no newData().children().key.isString() and newData().children().key.length(), etc...

Upvotes: 0

Views: 41

Answers (1)

Renaud Tarnec
Renaud Tarnec

Reputation: 83058

So, if you want to limit the length of the ids of the favorite food nodes, as well as forcing the value for such a node to be 1, the following rules will do the trick:

{
  "rules": {
    "fav": {
      "$uid": {
        "$foodid": {
          ".read": true,
          ".write": true,
          ".validate": "newData.isNumber() && newData.val() == 1 && $foodid.length < 5"
        }
      }
    }
  }
}

Note that this rule will perfectly work for pushing only one node or several nodes.

You can try the following with the JS SDK:

var database = firebase.database();

var updates = {};
updates['fav/user456/AAA5'] = 1;
updates['fav/user456/ABVD'] = 1;
updates['fav/user123/YUIH'] = 1;

database.ref().update(updates);

Note also that you probably need to add the part for checking that the user is authenticated and write to his/her own parent node, i.e. ".write": "$uid === auth.uid".


Having said that, another approach could be to use a Cloud Function in order to write to the Realtime Database. In the Cloud Function you would:

  1. Fetch the list of food items (the "HUGE" list) through a REST API that you would expose from a server reading your sqlite DB;
  2. Verify that the node of the new fav food is within the list.

Of course, if the list is really HUGE, it might not be cost efficient... just an idea.

Read this article for more detail on this approach.

Upvotes: 1

Related Questions