Once someone shares the getDownloadUrl() link with the token parameter, anyone can access the object, the same as they would a public object. Is the only difference that that token can be revoked / changed? Or is it also easier to scrape / list objects in the bucket if they are public and don't require a token? I'm trying to understand the purpose of having a non expiring token.
If you are referring to security rules by "making public" (or making the bucket public) then that just states who can request the download URL. If the bucket is private or the security rules don't allow anyone to access it, then they cannot request the download URL. However is by chance the manage to get the correct token and URL then they will be able to access that file.
To summarize, if someone who is not authorized to get a file, they would have to rely on someone who can request the donwload URL to access it (they cannot directly get one themselves).
If you make set permissions of your bucket to "Public to internet" from Google Cloud console, then if you visit root URL of the bucket you can see the contents:
By default, Firebase Storage's permission is set to "Not public" so no worries about that.
Edit:
If you allow list (requires rules_version="2") then contents of your directory can be listed similar to this:
{
"prefixes": [],
"items": [
{
"name": "Files/-Mf1kIafT6BnVnhgDQT2.pdf",
"bucket": "[PROJECT_ID].appspot.com"
},
{
"name": "Files/-Mf1pmCQMBNEdkQAnUjk.pdf",
"bucket": "[PROJECT_ID].appspot.com"
}
]
}
Reference: Granular Operations
Related
I have some JSON data in Firebase Firestore. The JSON in that data is being used by both the frontend (React) and backend (Node.js) of my application. But some fields in the stored data should be private and not accessible by the users of the website but still need to be available in the backend.
The stored data is similar to this:
school/school_id/class/student_id = {
"id": 123,
"name": "rahul",
"roll": "1234",
"privateKey": "345"
}
Frontend requires everything but the private key and the backend requires everything. If I give all the data to frontend any user can get access to privateKey when visiting the website.
What should I do so that the private key in the JSON is not accessible/visible to the frontend?
What have I tried so far?
Till now, I have not tried anything per say but I have read the security rules documentation and also the documentation to toggle visibillity of the json field. But I dont think none of them would solve the issue. I also have thought to remove the private field from the json and store it in some other way and then access it but this way is not that correct and I dont want to go towards this option!
I am building a web site and decided to go pure HTML+JS with full Firebase so I don't have to implement a backend system to test new ideas. The use case for this question is that all users should be authenticated in order to get access to the pages (pretty standard security feature, right?).
To accomplish that, I am taking advantage of Google Cloud Functions to check whether a user is signed in or not before allowing access to the pages.
Here is the code implemented on firebase.json:
"hosting": {
"rewrites": [ {
"source": "/home.html",
"function": "home"
} ]
}
Inside the home function, I run the following code to check whether the Id Token is a valid one:
admin.auth().verifyIdToken(idToken).then((decodedToken) => {
const userId = decodedToken.uid;
})
The problem I am facing is that the value for idToken is invalid:
Firebase ID token has incorrect algorithm. Expected "none" but got
"RS256"
I tried to copy & past the value from result.credential.accessToken, but I still get the same error message.
firebase.auth().getRedirectResult().then(function(result) {
if (result.credential) {
var token = result.credential.accessToken;
}
});
Any help will be very appreciated.
Thanks!
I understand that you direct the HTTPS requests to your home HTTPS Cloud Function.
You should pass the Firebase ID token as a Bearer token in the Authorization header of the HTTP request, as explained and demonstrated in the following official Cloud Function sample.
It is hard for me to understand why the simulator puts me off for this input
I supplied this auth token body:
{
"account": "7xms2zm6noz03f2mvn",
"playerId": "d3221a31-263c-4629-92fb-6cac89b67088"
}
I am using custom authentication to supply the above blob.
Please see the attached screenshot for how the simulator treats this:
This is my database tree:
What am I missing?
It appears that when you use the simulator, the "auth token payload" is actually at the auth level, not at the auth.token level.
So, you need to simulate the auth.token.account claim like this:
{
"token": {
"account": "7xms2zm6noz03f2mvn",
"playerId": "d3221a31-263c-4629-92fb-6cac89b67088"
}
}
You can tell it is (apparently) misnamed because if, for example, you select the "Google" provider, then provider and uid are both at the top level of this blob (which can't be modified), and that is where you would expect to find them.
Likewise this image from this blog post shows the token block as a sub-block in the simulator.
I am following the following Google Cloud Vision quickstart:
https://cloud.google.com/vision/docs/quickstart
This is using the API Explorer, and I get
Error Opening File
I have created a bucket named vision2018, and checked Share Publicly for the file.
My portion of the request related to the file is:
"image":
{
"source":
{
"imageUri":"gs://vision2018/demo-image.jpg"
}
}
The response I get is:
{
"responses": [
{
"error": {
"code": 5,
"message": "Error opening file: gs://vision2018/demo-image.jpg\"."
}
}
]
}
}
What do I need to specify in order to access files in my GCP storage?
Alternatively, I read other Stack Overflows that talk about GOOGLE_APPLICATION_CREDENTIALS, Simple API Key, and "Create Service account key and download the key in JSON format", ... but these seem to be giving commands in the shell, which this quickstart doesn't even open.
Is there initial setup assumed prior to the quickstart?
I am not ready to call the api from code
You might want to doublecheck your request. I went to the quickstart, replaced the placeholder imageUri with gs://vision2018/demo-image.jpg and it worked just fine. The error message you posted is what would be displayed if you had given gs://vision2018/demo-image.jpg\" instead.
Regarding the second part of your question: these are authentication methods. In this particular case, under Authentication you will find a drop down which lets you chose between API key and Google OAuth 2.0. If you chose the former, you don't need to do anything as a demo key will be used just for the purposes of the quickstart. If you chose OAuth 2.0, a popup will appear prompting you to authenticate with a google account. All in all, what you need to do is follow step-by-step the instructions given by the quickstart.
I was receiving a similar JSON response from the Google Vision API:
"error": {
"code": 7,
"message": "Error opening file: gs://bucket/file.jpg."
}
The fix was to set the GCS file's permission to public-read:
gsutil acl set public-read gs://bucket/file.jpg
Finally I investigated what happened. The problem is that your API token is only grant for process the image (allow right to use OCR engine), but that API is not also for accessing object in GS.
Therefore "message": "Error opening file:
The problem is similar with this post:Authorize Google Cloud Vision API to Google Storage image Maybe the error message is a bit dumb than many years ago.
The solution also mentioned in the answer section, but if you want some thing more clear (expose security side-effect) here it is: Set GCS read-only public
Reason I want to keep using API because it's better for use it in mobile application, we cannot give the OAuth2.0 to any phone. However, still find a way to secure the read-public bucket.
I'm sure I'm missing something wrt Firebase Storage rules, but I've done the following:
STEP 1
Firstly I set the following Firebase Storage rule:
service firebase.storage {
match /b/{bucket}/o {
match /items/{dev_key}/{perm_id}/{file_name} {
allow write: if request.auth.uid == dev_id;
allow read: if request.auth.token.permId == perm_id;
}
}
}
I expected only signed in users with a custom claim permId matching the relevant location to be able to download the file, allow read: if request.auth.token.permId == perm_id;.
So, I then set a custom claim in Cloud Functions on a user as follows:
STEP 2
admin.auth().setCustomUserClaims(uid, {permId: '1'}).then(() => {
// send off some triggers to let user know the download is coming
admin.database().ref(`collection/${uid}/${itemId}`).update({
downloadReady: true
});
});
Then I signed the user out and signed back in again... which set the custom claims.
I checked that they were set in Cloud Functions as follows:
STEP 3
admin.auth().verifyIdToken(idToken).then((claims) => {
console.log("--------------claims -------------");
console.log(JSON.stringify(claims));
});
And I saw in the claims string... permID: "1"
On the client side I then requested a downloadURL (here is hopefully where I'm going wrong)... I expected this to not be the public download url but rather the download url that the Firebase Storage security rules will check:
STEP 4
var pathReference = storage.ref('items/<some-key>/1/Item-1');
pathReference.getDownloadURL()
.then((url)=>{
console.log("url: ", url);
})
The url I received from this call gave me this link
https://firebasestorage.googleapis.com/v0/b/emiru84-games.appspot.com/o/games%2FcfaoVuEdJqOWDi9oeaLLphXl0E82%2F1%2FGame-1?alt=media&token=45653143-924a-4a7e-b51d-00774d8986a0
(a tiny little image I use for testing)
So far so good, the user with the correct claim was able to view this image
I then repeated step 2, logout/login again, except this time with a permId of "0". I expected the url generated previously to no longer work since my user no longer had the correct custom claim... and the bucket location was still at the same location (bucket/dev_key/1/filename) but it still worked.
If I repeated step 4 I got a new url, which then gave the appropriate 403 error response. However the old url still worked (I guess as long as the token parameter is tacked on). Is this expected, if so, I'm not sure I understand how the Storage security rules make a difference if the download url is public anyway?
Any help clearing my foggy brain would be appreciated.
The download URL in Cloud Storage for Firebase is always publicly readable. It is not affected by security rules.
If you don't want to allow public access to a file, you can revoke its download URL.