Following firebase documentation here, I have implemented the following security rule:
match /docs/{hashID} {
allow read, write: if hashing.md5(request.auth.uid.toUtf8()).toBase64() == hashID;
}
On the client side, I am using node-md5 to compute user's hashID and write to a document
firebase.firestore().collection("docs").doc(md5(user.uid)).writeSomething()
It is failing the security rule because the hashes don't match.
For uid "crz6KyreRCM4A0Qvk9EfeXHBLF43", my client md5 gives me "eee1f958a8c0a273f138bdee0167693d", while Firebase Rules Playground gives me "fbOLeadWz7YxnsGgfESBNg==". I've verified the client md5 value is correct using https://www.md5hashgenerator.com/. What am I doing wrong in the security rule?
Solved! The node-md5 output was a hex string, so the following rule is appropriate:
match /docs/{hashID} {
allow read, write: if hashing.md5(request.auth.uid.toUtf8()).toHexString() == hashID;
}
Furthermore, Firebase's toHexString() returns a result in all caps, so I need to do this on the client side
firebase.firestore().collection("docs").doc(md5(user.uid).toUpperCase()).writeSomething()
You are calling toBase64() unnecessarily. That's for converting byte arrays into strings, which can then be hashed. Since you're dealing entirely with strings here, there is no need to base64 encode anything.
Related
In Firestore i have the collection as "users" and document id are random numbers.
Now i need to allow the users to read the documents which contains field friend : true
I have tried following rules :
match /users/{document=**}
{
allow read : if request.resource.data.friend = true;
}
match /users/{document=**}
{
allow read : if resource.data.friend = true;
}
But yet i can't read the documents which contains friend = true.
Please help me
My first guess is that you're not passing the same condition in with the read operation. Keep in mind that Firebase security rules do not filter data. Instead they "merely" ensure that the read operation is allowed. So the second rules in your snippet allow this query:
firebase.firestore().collection("users").where("friend", "=", true)
If you don't pass the where condition, the read will be rejected as you're trying to read all documents, which the security rules don't allow. Only when the query and the rules match up, will the read be allowed.
Your first rules fragment cannot work in any case, as request.resource only has meaning in the context of write operations. From the docs:
request.resource
The new resource value, present on write requests only.
I want to make a rules in my firebase storage, and I want the filename of image stored in firebase storage contains uid of its uploader. usually in programming language I use .contains to check
service firebase.storage {
match /b/{bucket}/o {
match /eventThumbnail/{imageID} {
allow create: if request.resource.name.contains(request.auth.uid)
}
}
}
is this rules valid ? I can't find documentation about using contains in firebase storage security rules documentation. actually the rules is more complex, I still search what makes I fail to create an image, and I suspect that rules is not valid
You can use something like this:
match /{fileName} {
allow read: if fileName[0:6] == 'abcdef';
}
Documentation on string rules:
https://firebase.google.com/docs/reference/security/storage#string
The API documentation for security rules for Cloud Storage is here. The specific documentation for string type objects is here. You can see that in the documentation, there is no "contains" method for string. But there is a method for using a regular expression to verify the contents of a string using matches().
However, I think Frank is correct in his comment suggesting that it would be better to use a dedicated prefix for each user using their UID in the path of the file. For example "/eventThumbnail/{uid}/{file}" could be used to determine the permissions for any file organized under uid, where uid could simply be verified as equal to request.auth.uid (no substrings or regular expression required). This is also far more secure and reliable than checking file substrings.
Filename can now be used as UID and matched using Firebase storage split syntax found here.
I do also recommend rather using the path to prefix each UID instead, but if the files are already in a production environment the split syntax could prove useful.
Update:
Syntax on Firebase Documentation appears to be incorrect as indicated by this post
How can I verify if an incoming field is a valid e-mail? Is there a way to use string-functions or anything in Firestore security rules?
Example:
Let's say I have a Create-Request with a field called "email". In my Firestore security rules, I would like to check if the email is a valid email address:
contains '#'
ends with either .xx or .xxx (a casual country-domain-ending)
has a '.' before the last three or two letters of the email
the '.' does not follow directly after the '#' - at least two letters have to be in-between
So that e.g. example#emailprovider.com gets accepted, but not example#.com.
I know that this check is quite extensive and further would like to know if it makes sense to introduce such a validation to security rules?
You can use rules.String.matches.
See
https://firebase.google.com/docs/reference/rules/rules.String#matches
https://github.com/google/re2/wiki/Syntax
How to validate an email address using a regular expression?
Performs a regular expression match on the whole string.
A regular expression using Google RE2 syntax.
If you want to set only email address then It's necessary to validate the field as email address.
I found an example of a regex (and adjusted a bit):
^[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,5}$
The source of this is at the bottom of the following page:
https://firebase.google.com/docs/reference/security/database/regex
You should also take into account the note as well:
Note: THIS WILL REJECT SOME VALID EMAILS. Validating email address
in regular expressions is difficult in general. See this site for
more depth on the subject.
I'm trying to use the request.resource.crc32c from Firebase Storage rules (https://firebase.google.com/docs/reference/security/storage/#properties_2)
My goal is to have a rule like:
match /blobs/{hash}/{fileName} {
allow read;
allow write: if request.resource.crc32c == hash;
}
But I'm getting permissions denied for every write I'm trying. The Firebase documentation doesn't give any information about the format of the CRC32C hash: Is it hex, int, uint, base64 or something else ?
It's the big-endian representation of the crc32c value encoded as a base64 string (NOT base64url)
I'm using the Authorization header with the Basic type for authentication.
I'm following the HTTP Basic authentication specifications which states that the credentials should follow this form -> userIdentifier:password encoded in base64
We are using an email as the user identifier and according to the email format specification, the colon(':') character is permitted.
The colon(':') is also a valid character in the password.
Knowing this, I'm looking for a creative way to parse the credentials part of the header that uses a colon(':') as the separator between userID and password.
In this case it's simple -> francis#gmail.com:myPassword
This is where it gets complicated -> francis#gmail.com:80:myPasswordWith:Inside
francis#gmail.com:80 is a valid email according to the email format specification even though this is not used very often. So where do I know where to split ?
We have made the decision not to accept an email containing a ':'. But we want to notify the user that his email is not valid, how can we ensure that we are splitting the string at the right place ?
Hope I asked my question in a clear manner, don't hesitate to ask for more details
Thank you
Don’t notify the user that the email is invalid. Split according to the RFC 2617 rules (everything after the first colon is the password), then try to authenticate, fail, and return a generic “authentication failure” message.
A situation where john#example.org:80 has password secret and john#example.org has password 80:secret at the same time, seems unrealistic.
If you require your users to register, you probably do it with some other mechanism (forms?) where you can easily separate the username and tell that it is invalid.