k29
k29

Reputation: 1909

How to detect forward slashes in Firestore document IDs with security rules?

I have a usernames collection. Each of its documents has an id equal to the username of an app user, and a single field userId, which is a document ID in a parallel users collection. If a user changes his name to John52, I create a new document with an ID John52, delete the old username document, and update the username field in several other documents in other collections using a batched write.

I would like to prevent the creation of certain usernames, and I use the following security rule to achieve that:

function isSignedIn() {
  return request.auth.uid != null
} 

function existingData() {
  return resource.data
}

match /usernames/{username} {
  allow get: if isSignedIn();  
  allow create: if username.size() >= 5 && username.size() <= 12 &&
                   username.matches("[[:alpha:]]*") == true &&
                   username.lower().matches(".*duck.*") == false;
  allow delete: if isSignedIn() && request.auth.uid == existingData().userId;  
}

It is supposed to allow the creation of a new document only if its ID is: (1) between 5-12 characters in length, (2) alphanumeric, and (3) not a bad word.

The problem is that this rule does not prevent usernames with forward slashes at the beginning or the end of the otherwise valid text, e.g., usernames /John52 and John52/ are both allowed. The document ID that I can see in the Firebase console is still John52 (without slashes), so I suspect that perhaps Firebase somehow treats those slashes in a special way, or drops them, and the {username} in the security rule has no slashes anymore.

I know that document IDs should not contain forward slashes. However, if the ID gets set from the client, what is the best way to prevent it, and is it possible to do with the security rules?

Upvotes: 1

Views: 242

Answers (1)

Dharmaraj
Dharmaraj

Reputation: 50890

However, if the ID gets set from the client, what is the best way to prevent it, and is it possible to do with the security rules?

First, the Firebase client SDK will remove the slashes from the document ID before that create/update document request is sent so value of username will never contain /.

If someone tries to use the Firestore REST API and bypass the SDK, they'll get an error says:

{
  "error": {
    "code": 400,
    "message": "Resource id \"/test\" is invalid because it contains \"/\".",
    "status": "INVALID_ARGUMENT"
  }
}

The best you can do is replace / with any other character (e.g. =) but render it as a slash on frontend (i.e. .replace('=', '/')).

Alternatively, you can store all the username in an array but a document only has 1 MB max size limit and won't scale well. Routing write requests through a Cloud Function would be another option where you can encode the username into a base64 string and use it as document ID. You can then decode it while rendering.


The client SDK will remove only trailing or leading slashes.

const docRef = doc(db, 'books', '/test/')
console.log(docRef.id) // <-- 'test'

await setDoc(docRef, { ... })

If you have multiple forward slashes and create odd number of path segments, then it'll throw an error as the path would represent a collection and not a document.

Upvotes: 1

Related Questions