Cloud Storage for Firebase - Security Rules

Previous posts discussed security rules for Firebase Realtime Database. And in this post we’ll talk about security rules for Cloud Storage for Firebase, formerly named Firebase Storage.

Security rules for Cloud Storage are path-based and you combine the path’ing with rules on authorization and data metadata.

So let’s get started!

By default, the rules allow any user who is authenticated to write to the database:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

As noted by the Cloud Storage for Firebase documentation, since Cloud Storage shares a bucket with your default Google App Engine app the data for that app also becomes public:

Screen Shot 2019-07-21 at 10.15.24 AM.png

Let’s use the same rule pattern as previous posts and allow only ourselves to read and write:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth.uid == "1234567890123456789012345678";
    }
  }
}

So now we’ve added a measure of protection for our Cloud Storage for Firebase.

With the above rule we can begin development work and read and write as ourselves.

Let’s keep going and define rules for a specific path. For this example, assume that users will write to the following path:

      /Photos/<userUUID>/filename

We can add an additional rule specific to this path below the existing one:

rules_version = '2';
service firebase.storage {
  match /b//o {
    match / {
      allow read, write: if request.auth.uid == "1234567890123456789012345678";
    }
    match /Photos {
      allow read, write: if request.auth.uid == "1234567890123456789012345678";
    }
  }
}

Now is a good time to reintroduce the rules Simulator. In the Simulator set the following values:

  • Simulation type = create

  • Location = /Photos/1234567890123456789012345678/yourfilename.jpg

  • Enable Google Authentication and enter your UID

Run the Simulator and you’ll see that the first rule is the one that’s matching.

Screen Shot 2019-07-21 at 12.48.19 PM.png

Remember: once a user obtains write access it is valid to any depth of the tree!

This is a great example of why you should run Simulator for all of your rules as you develop them. Its important that you have rules that allow your users to read and write but its even more important to know which rule allowed it!

We need to omit the parent matcher on root and enable our matcher for /Photos. To do that:

  • Remove the matcher for “/” altogether

  • Lift the “” from all paths notation and append it to /Photos

Our rules are now:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /Photos/{allPaths=**} {
      allow read, write: if request.auth.uid == "1234567890123456789012345678";
    }
  }
}

Try it in the Simulator - it passes!

Screen Shot 2019-07-21 at 12.53.04 PM.png

We could be done now but we should go a little further.

We know that our path will be /Photos/<userUID>/filename so we should lock the rules down to the full path that we know we are using. So let’s add wildcarding that will resolve the auth UID of the user and match it in the path:

    match /Photos/{userUID}/{allPaths=**} {
      allow read, write: if request.auth.uid == userUID;
    }

Try the Simulator - it works!

Screen Shot 2019-07-21 at 12.53.04 PM.png


Publish your rules to save them!

At this point we’ve locked down our Cloud Storage for Firebase first by matching our own user UID and then to specific pathing; we’ve begun to refine our rules; and we’ve developed habits for using the Simulator to test our rule and ensure that only the rule that should match is the one that matches.

Let’s go!