Reputation: 501
So I have a function in my website which allows users to upload a profile photo. The photo is stored in a folder in my Firebase storage under their UID and then once successfully uploaded, the downloadURL is stored under their UID in my Firebase realtime database like so:
database.ref('users/'+firebase.auth().currentUser.uid).update({
profilePhoto: downloadURL
});
Now I have some rules already in place so that only the auth UID can write data to themselves.
{
"rules": {
"users": {
".read": "true",
"$user": {
".read": "true",
".write": "auth.uid == $user",
"profilePhoto": {
".validate": "newData.isString()
},
}
}
}
}
Work great but how can I make it so only URL's coming directly from my Firebase storage are written?
For example, a user with knowledge could simply run the script above and change the data to something outside of the storage or just some random text which breaks the profile image when I fetch it.
One thing I thought of doing is adding the Firebase storage 'base path' as a validation like so:
"profilePhoto": {
".validate": "newData.isString()
&& newData.val().contains('mystorage-12345.appspot.com/')
}
But is there a better way of doing it? Will the 'base path' ever change down the line?
Upvotes: 1
Views: 139
Reputation: 3842
There are no combinations of rules that you could use to prevent a user from linking to a malicious file or entering gibberish. Since you're open to better methods, here's my standard pattern for handling uploads like this.
First, decide on a URL structure for your profile photos where the photo will be stored in your bucket with an easily guessable format. My preference is:
/u/<uid>/profile_<width>x<height>.jpg
Second, provide a signed URL for your user to upload their image to. This might be in a separate bucket that, when a new object appears, triggers a function resize the photo and then moves the resized photos to /u/<uid>/profile_<width>x<height>.jpg
with the relevant widths and heights.
Finally, there's no need to store the URL of the profile photo because any user's profile can be displayed like this:
<img src="https://<bucketUrl>/u/<uid>/profile_512x512.jpg" onerror="this.src=' + "/img/default-profile.jpg" + '" />
What's happening here is it first attempts to load the user's profile photo using the guessable /u/<uid>/profile_<width>x<height>.jpg
pattern and if it fails to load, for any reason, display a default photo in its place.
Advantages
Upvotes: 1