Securing your Firebase app (part 1)
Please misunderstand me in the right way when I say: sometimes I get a bit obsessive over details, I need control and I have a weird passion for data structures and protocols.
I normally try to avoid saying it like that in public because it makes me sound like Patrick Bateman. What actually I mean is that I like well organized systems, and that’s also why I particularly like to work with databases and communication protocols.
This article is the first of some articles that will help you to add some helpful patterns of security, control and maintainability of your application. When I say security, I basically mean “sanity”, because security is about awareness of what is normal and paying attention to what is not. I will focus on the awareness part; making life easier for yourself to be aware.
They’re easy to read when they’re few and your app is in an early stage of development. Soon enough, after your first limping feature, you start having more dependencies and relational constraints. And that’s when the struggle starts.
The Firebase database rules language is very small and well defined. But for any data model worth its salt, it is possible that it is looking something like a bird’s nest of entangled validation rules and read/write conditions that are cross referencing other paths and authorization states.
This is why you shouldn’t write database rules by hand.
If you haven’t already heard of the Bolt Firebase security rules compiler, you should check it out and start using it.
As with the actual database rule language of Firebase, the Bolt language is also very small and well defined. Ironically it’s written by Firebase team, and compiles into another of their own languages. One would perhaps think that if Bolt was created to solve a problem, then perhaps it could just replace the JSON definition language that is the root of the solved problem. By making Bolt the standard language we would make do without the need for a compiler.
Remember that Bolt is at the time of writing only Beta and it still contains a few inconsistencies such as non-mandatory semicolons, non-mandatory syntax in unambiguous context such as return statements and certain keywords. It is recommended that you manually check the output that is generated by the compiler. Sanity and control doesn’t come from assumptions.
The way I use it is that I will only add the .bolt rules file to my git repository and I put the .json rules file into my .gitignore file. In the gist below you can see an example of a Bolt file and the resulting JSON rules.
Below is an overly feature packed example that showcases a few useful ways of using Bolt. The example is simplified nonsense. But if you use your little imagination, you see the power of how this can and will scale in a larger application.
As you can see from the example above, it’s possible to both define Bolt rules as simple “read&write” or using the three write aliases: create, update, delete.
You can not use “write” if you’re choosing to go with the aliases. Because they will translate into smart definitions that only allows for write actions according to their respective name. And “write” allows for all kind of write, which includes all the three aliases.
Use types and inheritance. They’re your best friend for making very complex validation rules. A simple example given above is maintainability of types such as Email or MD5 where you’d like to have a regex validation. The Bolt language allows you to make the definition in one place so that you don’t need to repeat yourself or have to maintain an expression spread out in several places of a JSON file.
With some smart type definition you can even set up immutable fields that can only be written once (see InitialTimestamp in the example above) or generic types
Use the Bolt compiler! Don’t write your own database rules. Seriously! The maintenance of your database rules will become magnitudes easier and the readability will increase. This will indirectly make your security design safer and more robust since you can spend more time expressing your intentions and less time untangling some long logical expression.