Be careful when going client only (Firebase)

Firebase, the scalable real-time backend

Developing client-only applications with JavaScript and services like Firebase offer a nice way to “build apps without managing servers”. However, sometimes it’s not a bad idea to have some controlling server between your client and your data storage.

When recently browsing over firebase.com and looking at one of their code examples to get a simple overview of their service, I was surprised how easy it is for clients to interact with the data storage/backend.

The code example I had a look at is a service to show “online presence” of users. Whenever a user visits the page, he is prompted for a name which is then saved in the data storage and therefore also send to all users which use this firebase reference (usually all users on a page would use the same reference so you can see all users which are currently online).

Establishing a connection to the Firebase-Reference is a simple one-liner in Javascript:

var userListRef = new Firebase("https://v47p4chbar8.firebaseio-demo.com/");

This reference allows the client to sent/update/remove data in the data storage. Easy! The first thing which came to my mind is the possibility of an attacker to flood the online presence list. Let’s try this. Pop up your developer console, open the Javascript console and try the following:

userListRef = new Firebase("https://v47p4chbar8.firebaseio-demo.com/");
for(i=0;i<100;i++){
    userListRef.push().set({name:i, status:i})
}

Four lines of code, 100 online users. Not bad, right? Note: A real use case could make use of firebase security rules, so only authenticated users can be present. The chat application, which is a more realistic use case, is also floodable. Are there security rules to prevent this massive flooding?

Let’s dig a bit deeper in the source. I had hoped to find something where I can directly inject some HTML over the name or status, but when you look at how a name/status is added, you can see that the jquery .text(var) function is used, so you can’t inject HTML here:

userListRef.on("child_added", function(snapshot) {
    var user = snapshot.val();
    $("#presenceDiv").append($("<div/>").attr("id", snapshot.name()));
    $("#" + snapshot.name()).text(user.name + " is currently " + user.status);
});

But this 4th line looked interesting. They are using snapshot.name() in the jQuery-Selector. The .name() function does not return the previously set name, but it returns the name of the child location (the one which is set automatically by calling .push()). BUT.. You can set this by yourself.. ;) So lets get back to our developer console:

userListRef.child("foo,body").set({name:"I'm the new Body-Content."})

Using the command above, the following is executed at each client:

$('#foo,body').text(...)

So we are setting the content of #foo (does not exist) and <body> (does exist). The whole body is now replaced! Let’s revert this:

userListRef.child("foo,body").remove();

Which is executed in the following lines on the client:

userListRef.on("child_removed", function(snapshot) {
    $("#" + snapshot.name()).remove();
});

Oh, we just removed the <body> off all ours clients..

Sadly (for us) Firebase used jQuery 1.9, which is not affected by the selector xss bug. Otherwise we could have executed code to all other users with this simple line:

userListRef.child("foo,<img src=x onerror=alert(1)>").set({name:1})

Checking Firebase customers

As I mentioned above, Firebase allows users to add security rules to restrict access. However, I had a quick look at some of their listed Customers to inspect if they are using their security rules correct.

I just checked some customers which are using Firebase before I need to register to their service, so I could have a quick look at their setup.

After I found the Firebase-Reference on the first page, I opened my dev console and tried to attach to their data. I’ve never access user data so easy..

So lets check a second customer listed on their Customers page:

While it is super easy and fast to develop apps with firebase, it looks like some developers do not take the extra effort to set up security rules. Maybe Firebase should take the initiative and require security rules to be set.

Replacing your backend with a cloud backend is a nice way to go. But keep in mind that you do not only have to validate data coming from the client. You also should validate data coming from your data store now. And please do not forget to set up rules, because making data not visible to the user is not the same as restricting access to the data, especially when we are talking about a client accessible data storage.

Note: I already contacted Firebase and one of their customers regarding the issues a few days ago. The second page had no contact information.

See Also