Realm.io versus Google Firebase — a security perspective
This article is one of a series of short pieces in which I try to compare the features of Google’s Firebase product to Realm Cloud platform. Both of these offerings provide real-time data-base functionality for mobile applications; both are referred to as a backend as a service (mBAAS). This piece is focused on the differences in the security models between Firebase and Realm Cloud.
More information about Realm.io can be found at
Realm: Create reactive mobile apps in a fraction of the time
Realm is a mobile platform and a replacement for SQLite & Core Data. Build offline-first, reactive mobile experiences…
More information about Google Firebase can be found at
Firebase is Google's mobile platform that helps you quickly develop high-quality apps and grow your business.
I have been an iOS developer for the past seven years and have used both Firebase and the Realm object database extensively since 2015. While working as the mobile architect for Needly, Inc in Santa Monica CA, I helped develop the Troop Team product and the WorkCoin market product. Both of these products relied on Firebase as a backend and used Realm as the local object data base. Today, I am managing a new startup called Cosync, Inc that is developing a collaboration platform around the Realm Cloud platform.
Realm.io is an interesting company. It was founded in 2011 by Alexander Stigsen and Bjarne Christiansen with the intent of providing a mobile database for both iOS and Android. The company was venture funded and successfully raised over $20 million as of 2015. Early on, cloud based mobile applications needed to cache large volumes of data without having to go to the web every time they needed it. Even though both Apple and Google provided local data bases as part of their development frameworks; with modules like Core Data, Realm was the first truly cross platform solution to solve this problem. Furthermore, Realm was given away for free to the development community and was very easy to get up and running in Objective C, Swift, or Java — depending on the mobile device. I started using it about three years ago. Today there are over two billion Realm installs in the world and over a 100K Realm Developers. Talk about under the radar! They only have 33 employees.
The Realm company released a paid server version of the database called Realm Platform, which was a full backend as a service solution in 2017. Their hosted solutions are very affordable and start at about $30/month for three Realm instances — a Realm instance is a full database which can be assigned to a single mobile application. In most cases, the three instances correspond to the development, staging, and production databases for a mobile app. The pricing is based on a combination of installed client instances and bandwidth usage. Like Google’s Firebase, pricing scales with usage. Unlike Firebase though, customers can choose to host the Realm Platform on their own servers should they need to. This is extremely powerful, as it caters to a number of security sensitive customers, who are not able to use Firebase because the data is hosted by Google. More importantly though, most app developers who use Firebase as their backend real time database also use Realm as their local database to cache network data. This typically requires a somewhat complex translation layer, that each developer must write, between Firebase and Realm in both directions. If a developer uses the Realm Platform instead of Firebase, this extra translation layer (which I can attest is the source of many bugs) simply goes away.
When it comes to security, both Firebase and Realm provide mechanisms to lock down the data. Both of these products provide a NoSQL real-time data base on the server-side to service users logged in to various client-side devices. The problem in a nutshell is as follows. A real-time data base consists of a collection of tables, which conform to a schema, and each table contains a number of properties, which descibe the types for the objects belonging to that table. The data base instance contains a number of registered users, some of whom may be logged in on client devices, who access objects in the tables. The security problem is two-fold: firstly, objects that belong to tables must adhere to specific formatting requirements as specified by the schema; secondly, user access to this data must be tightly controlled in accordance to the privilege level of the user. The first is a validation problem, while the second is an access problem.
A Firebase database is essentially a gigantic piece of JSON data. The first level of this JSON data contains the outer most tables of the database. For example, consider an application called ‘SimpleProjectManager’ that has three types of objects: users, projects, and tasks. The Firebase data base would be structured as follows:
The Firebase rules determine both the structure and the user access to the Firebase JSON data. The rules themselves are specified in JSON. Structure and validation are specified in a hierarchical fashion, whether data is readable or writable is specified at each level in the structure tree.
The property validation on objects is fairly straightforward; each property is spelled out along with its associated data type. If a client side write transaction does not conform to the property structure as specified by the rules, the write transaction is rejected, and the data base is not update. Read and write access is specified at each point along the path with a “.read” or a “.write” conditional expression. If the conditional expression evaluates to true, the user is granted read or write access. In Firebase, a user cannot query a table if he/she does not have read access to the table. The user can however read a single entry in the table if he/she has read access to that entry.
In Firebase, permissions cascade hierarchically from the top of the path tree downwards. Essentially, this means that if a permission is granted at a parent level, that permission is granted to the user for all lower levels in the tree. In other words, if a permission is granted to a parent, it is automatically conferred to a child. In Realm Platform this is not the case. In practice, although the Firebase security model may reflect the manner in which civil rights are conferred in a free society, a child has the same rights as his/her parents and more, it not well suited to conferring fine grained privileges to the data. In software, segregation and restriction of privilege are the keys to a secure system — think of the military and not a University campus. A common mistake made in Firebase, by programmers working the midnight shift, is to insert a “.read”, true statement at the top of the file because some permutations of permissions is blocking their updates — thereby making the system wholly unsecure.
The other complaint about the Firebase rules language is the sheer complexity of it all. In Firebase, rules are coded up as a collection of complex conditional statements that may either evaluate to true or false depending on the status of the authenticated user. These conditional statements can get quite unwieldy depending on the security requirements. Reading Firebase rule statements is a lot like trying to parse XSLT stylesheet statements or debugging LISP code; they tend to be just long strings of parenthesized data. On the WorkCoin project I worked on for Needly, Inc back in 2017, the write rules regulating the job’s table were as follows:
Although this might seem logically correct, it is very difficult of another programmer to actually validate the security requirements as specified — let alone debug them! There exist third party tools like the BOLT compiler that simplify the writing of Firebase security rules — but since they translate down to them, the complexity never really goes away. In practice, usually one programmer in a Firebase project team takes care of the security rules, and the others just pray that he/she is doing their job right.
The topology of a Realm data-base is different from that of Firebase. The highest-level enclosure is called an instance, which maps to a single application. An instance has a URL that uniquely identifies its server location. Inside each instance, there are a set of realms and a set of registered users. Each realm has a unique path within the instance; the default realm has a path called ‘/default’. Inside each realm, there can exist a number of classes and objects that belong to them. The structure of the objects within a realm, i.e. a description of the classes, are specified with a JSON schema. This is similar to the firebase rules language. For example, the Realm schema for the user class would be as follows:
The schema specifies the structure of the data, it does not handle validation, nor does it control access to the data — which users are permitted to read, write, or modify.
Unlike Firebase, Realm handles most functions through code, and not through an admin control panel. That being said, Realm does offer a tool called Realm Studio, which provides an interactive graphical user interface onto the Realm database — much in the same way that a Firebase console does. Realm Studio is a client-side desktop application, not a web application.
Typically, a Realm Platform application will have both a client-side piece, written in Swift for iOS or Java for Android, and a server-side piece written in Node.js. The purpose of the server-side piece is to perform validation and centralized post processing on the database. From Realm’s perspective however, the server-side piece is itself just another client logged in as administrative user. In fact, the server-side piece can actually run on a desktop computer, and usually does when the application is being developed. In Realm, all users are either regular users or administrative users; the later have full access to everything within an instance. It is customary to have only one administrative user, which runs on the server-side Node.js code. From a security standpoint, mobile clients should typically only support regular users. On initialization, the server-side piece will set up all access control rights for users on the database. Unlike Firebase, setting up the access control rights is done in code.
Realm provides two mechanism for regulating access control. The first is path level permissions, which applies to the various realms within the instance. The second is fine-grain permissions which applies to classes and individual objects.
Path level permissions control which users in the instance can access a specific realm. Users are granted an access level which control:
- Read– whether user can connect and read the realm
- Write– whether the user can write to the realm
- Manage– whether the user can change permissions on the realm
In a normal application scenario, there is one default realm (at path ‘/default’), and private realms for each user. When a user creates a private realm, it exists at a path called /<userId>/privateRealm. When a user is logged in, this private realm can be accessed with the path /~/privateRealm — the tilde ~ is a shorthand for the user id. The private realm is usually only accessible for the user, and by the server. Other users should never be given access to a user’s private realm.
Starting with Realm’s version 3.0, the company introduced the concept of fine-grain permissions. This is a role-based permission protocol that takes security and access control to a whole new level. Fine-grain permissions allow the system to regulate access control at the object level — hence their name fine-grain. Although this can be implemented circuitously in Firebase, by defining a role table that ties into the data-base rules, it is not a built-in feature.
Fine grain permissions are based on the concept of roles. Users are assigned roles, which are given permission to access a realm, a class, or an object. Realm defines a number of predefined roles, notably the role ‘everyone’ and a unique role for each user called ‘__User:<userId>’. The type of permissions granted to a role include:
On initialization, the server administrator will change the permissions associated with ‘everyone’ to be very restrictive, and then define a number of roles and their access privileges for the rest of the users.
In Realm, the fine grain permissions operate at one of three levels:
- Realm level
- Class level
- Object level
The fundamental difference between Realm and Firebase, when it comes to access control, is that with Realm, privileges granted at one level can be restricted at a lower level. This is conceptually the opposite of Firebase, where privileges granted at a root level are automatically conferred to objects at a lower level. The Firebase approach might seem right when running a free and open society, but when it comes to implementing software security, a military compartmentalization is far more appropriate. Even in real life scenarios, just because you are allowed to enter a bank does not mean that you are allowed to enter the vault — even though you would have to be granted permission to enter the bank to be able to enter the vault. In my three years of programming Firebase, I cannot stress how difficult it has been to work around its policy of cascading privileges.
A realm level permission may give the user (or the role) the right to read the realm. However, this permission can be overridden at the class level, and furthermore at the object level. The right to read is really only an option to read — this is important. Furthermore Realm distinguishes between the write capability of creating an object versus the write capability of updating it or deleting it — Firebase makes this distinction but only marginally with the data() and newData() constructs. Also, Realm does not require that a user be able to read all objects in a table to be able to query them. For objects that are inaccessible, a query will simply not return them. In Firebase, a table must be fully readable for a query to succeed — so the choice is to expose everything or nothing at all. Lastly, Realm offers a meta level construct insofar as security is concerned with its canSetPermissions and canModifySchema permission, which grants users at a fine level whether they are allowed to modify the permissions on a realm, a class, or an object.
The one downside to Realm is that all the permission granting is done in code rather than through some administrative console. Implicitly, this requires programmers rather than systems administrators to manage an application database’s permissions and security requirements. This is a shortcoming both from a maintenance and from an auditing perspective. Security processes that are embedded in code are much more difficult to track. On the plus side, since Realm’s security model is code based, the full power of a programming language can be brought to bear on solving the problem — unlike Firebase’s complex conditional statement language.
In conclusion, the combination of both path level permissions and fine grain permissions makes Realm an easier system than Firebase to encode and enforce security requirements. Having programmed both systems for the last three years, I feel that it is easier to develop highly segregated security rules with Realm than it is with Firebase.