Firebase storage bucket grants access to some countries only but not others - google-cloud-storage

I have a Firebase Storage bucket located in australia-southeast1. When a user creates an image with my app, that's where the file goes.
Then the image is supposed to be shareable through a download link obtained with the getDownloadUrl() function (so a valid public link with token is generated).
However, when I share that link, people from Australia, Asia and US could access it. But users from EU, Canada, North Africa, etc. couldn't. See an example here.
My guess is that this is because of my bucket's default location. But, I am puzzled since the documentation does not mention any geographic restrictions of this kind and I have not set any specific limitation. It is not even a matter of speed of access, it is just simple access.
Shall I create multiple buckets in multiple locations and duplicate all the images in all buckets to ensure access independently of the user's location?
Edit: Details of debugging attempts
Those who can't access only see a white screen.
I tried to replicate the issue using VPNs of those countries, and checking the inspector, the console says nothing and for the network part, the GET request gives a '200 Connection established' status but 0 Byte transferred.
Then I went on and tried to do the GET request from within my app to
display the image in it. This time, I got in the console a
'Cross-Origin Request Blocked...(Reason: CORS header
'Access-Control-Allow-Origin' missing)' message and still the same
status in the network tab. The app is able to successfully open the
image on VPNs of locations that can view it.
Also, I already checked the CORS configuration in Google Cloud and they look fine and allow
my app's domain to access to the bucket. I even tried to allow any
domain (by setting the origin parameter of the CORS to "*"), just to
see what will happen, but the behaviour remains the same (I don't see
why it would work for some and not for others anyway).

This is usually not an expected behaviour. One reason for this may be the CORS configuration is not properly set. As an initial troubleshooting step I would recommend you to configure it as stated here and in a more detailed manner in this document.
If the above doesn’t help then you may require to raise a support ticket with Google Cloud Platform Support to investigate the issue in detail and find a root cause. If you don’t have a valid support package you may raise a Free Firebase Support ticket.

Related

What is the best approach to stop your platform's users to "sniff" the frontend requests to backend and modify them?

So I have a platform that works like this: Users can create accounts by logging in with their Google (I USE AUTH0) and then they can create "Projects" which contain lots of other unimportant stuff regarding my current problem (like todo lists, ability to upload files etc; they can also Edit the project by changing some of it's attributes like name, description, theme and so on). There is a home page where everyone can see each other's projects and access them (but not upload files, change the tasks in the to do lists; this is possible only by the person that owns it).
By using a tool like Burp, people can see the request made from frontend to backend, for example when accessing one of the projects, and modify it on the fly.
This is what it looks like inside Burp when they access one of the projects:
As you can see there is a Get request to /projects/idOfTheProject; they can replace the GET with DELETE for example and they will successfully delete it; they can also see what is sent to the backend when a project is edited (name changed, description, thumbnail picture etc) and change anything they want about it.
How should I prevent this?
What I've looked at so far:
a. JWT - Probably the best fitting for my situation, but required the most work to be done (as I already have my platform almost finished with no such a security measure implemented yet, so I may need to rewrite a lot of things in both backend and frontend)
b. Sending the user's id that initiated the action as well to the backend and verify if it has the necessary privileges - the worst solution as users can access each other's profile and see the id, then just change another field in the request's JSON
c. Have a sort of token for each user and send that instead of the user's id - in this way somebody can't get your token by just looking at the communication between frontend and backend (only if it is using YOUR account). That token should be taken maybe somewhere from the auth0 when they create their account? If they provide something like that; or I can just create it myself and store it alongside the other user variables. You would still see the requests in plain text but even if you modified something you would still have to "guess" the owner's token, which will be impossible.
For frontend I use NextJS and for backend Flask.
Thank you in advance!
The TL;DR is that you don’t. A determined user will always be able to see what requests are being sent out by the code running on their computer and over their network. What you are describing when asking how to prevent people from “sniffing” these requests is security through obscurity, which isn’t actually secure at all.
What you should do instead is have an authorization system on your backend which will check if the current user can perform a given action on a given resource. For example, verifying that a user is an administrator before allowing them to delete a blog post, or making sure that the current user is on the same account as another user before allowing the current user to see details about the other user.

How to make Google Cloud Storage direct download links compliant with ACLs?

If a .txt file is saved to GCS and clicked on through the developer console browser by an authorized user, the contents are displayed in the web browser. That's fine, but that URL can be sent to anyone, authorized or not, allowing them to view the contents of the file.
"Share publicly" is unchecked, and no changes have been made to the default ACLs. And this isn't specific to .txt files -- that's just the easiest way to replicate the behavior since they're displayed directly in the browser (so you can easily get to that URL).
How do I configure GCS to either disable direct download links or ensure they're compliant with ACLs?
EDIT: It appears that the link expires after a few minutes, which reduces the associated risk a little, but not entirely. I'm still extremely nervous about how easily an authorized user could use this to inadvertently provide an unauthorized user direct access to something they ought not...
Left vs. right-clicking on files
First, regarding the difference between left-or-right clicking: I could not establish a difference between left- or right-clicking on a filename in the Google Cloud Storage storage browser.
To verify this, I opened a Google Cloud Project and opened a private object in a private bucket and opened it using both methods. I copied the URLs and opened them in a Chrome incognito window, where I was not logged in, to verify that my ACLs were not applied.
I was able to see both of the URLs in the incognito window. After some time, my access to them expired. However, interestingly enough, my access to them expired just as well in the window where I was logged-in and authenticated to access Google Cloud Storage.
This is where things get interesting.
Security and ACLs for user data in Google Cloud Storage browser
TL;DR: I believe the behavior you observed, namely that the URL can be viewed by anyone, is working as intended and it cannot be changed beyond what Google Cloud Storage already does with automatic timeouts; let me explain why.
When you are browsing Google Cloud Storage via the Developers Console, you are using the storage browser on the domain console.developers.google.com which means that you are authenticated with Google and proper ACLs can be applied to allow/deny access.
However, the only things you can view on that domain are bucket names, object names, and metadata, not the file content itself.
If Google were to serve you file content on the google.com domain, it would create a security issue by allowing an adversary to force your browser to execute Javascript on your behalf with your Google credentials, thus allowing them to do anything you can do through the web UI. This is typically referred to as an XSS attack.
To disallow this from happening, Google Cloud Storage (and Google in general, e.g., cached web pages) serve user-originating data on a different domain, typically *.googleusercontent.com, where users can't take advantage of any sensitive cookies or credentials, since nothing that Google provides is served on the same domain.
However, as a result, since the data is being served from one domain (*.googleusercontent.com) but your authentication is on a different domain (*.google.com), there is no way to apply the standard Google Cloud Storage bucket or object ACLs to the file contents themselves, while protecting you from XSS attacks by malevolent users.
Thus, ALL users, even those that have direct access to the file, upon viewing them in their browser, will have the content served with a time-limited signed URL on a different domain.
As a side-effect, this does allow users to copy-paste the URL and share it with others, who will have similar time-limited access to the file contents.

Correct way to handle user permissions with Google Cloud Storage?

I'm quite new to Cloud Storage solutions, and I'm currently researching options to upgrade our current solution (we currently just upload on a SVN server).
What I have is a native application running on client computers, which will upload data to the Cloud Storage. Afterwards, client should be able to download and browse their data (source is not set in stone, could be a website or from other applications). They should not be able to access other user's data.
I'm not sure how I'm supposed to proceed. As far as I understand, the native application will upload using a Native Application Credential, using JSON.
Do I need multiple credentials to track multiple users? That seems wrong to me. Besides when they come back as 'users' through the web interface, they wouldn't be using that authentification, would they?
Do I need to change the ACL of the uploaded files afterwards?
Should I just not give write/read access to any particular users and handle read requests through Signed URLs, dealing with permission details by myself using something else on the side? (not forcing a Google Account is probably a requirement)
Sorry if this is too many questions, and thanks!
Benjamin
The "individual credentials per instance of an app" question has come up before, and unfortunately there's not a great answer. If you want every user to have different permissions, you need every user to be associated with a different account.
Like you point out, the best current answer, other than requiring users to have Google accounts, is to have a centralized service that vends signed URLs to the end applications. That service would be the only owner of all of the objects and would give out permission to read or upload as needed.

Static Web site served from Google Cloud storage in Google Apps Domain

It seems like this would be really, really easy - but I can't get it to work. All I need to do is to be able to serve files from Google cloud storage while restricting access to my google apps domain. I easily did this before using Google App engine simply by choosing that I wanted to limit access to my domain and setting the app.yaml appropriately. I can't find anything that tells me what I might be missing - I've tried using gsutil to set the ACL to restrict to my domain, which processes successfully through the command line, but then when I try to look at the bucket or object permissions through the cloud web console, I get "unexpected ACL entity type: domain".
I'm trying to access using storage.googleapis.com/bucket/object (of course with my bucket and object name) and I always get a 403 error even though I'm definitely logged in to gmail, and as the administrator of the domain, it seems like it should work because even if the ACL's were otherwise wrong (and I've tried it both with and without the domain restriction), and that it would work for me at least. The only way I can serve content using the above url is if I make it public - which obviously is NOT what I want to do.
I'm sure I'm missing something completely stupid, or some fundamental principles about how this should work - can anyone give me any ideas?
I'm not 100% sure what your use case is, but I'm guessing that your users are attempting to access the objects directly from a web browser. storage.cloud.google.com accepts Google authorization cookies, which means that if a user is logged in to an appropriate Google account, they can access resources restricted to certain users, groups, or domains. However, the other endpoints do not accept cookies as authorization, and so this use case won't work.
These users have permission to access objects using storage.googleapis.com, but doing so requires explicitly authorizing requests.
In othe words, a simple <img src="http://storage.cloud.google.com/bucket/object" /> link will work fine for signed-in users, but using storage.googleapis.com requires explicitly authorizing requests with via OAuth 2.

How to disable "Publish content to your Wall" when using Facebook's new Oauth2?

Right now when I use Facebook's new OAuth2 system, it tells the user that my app is requesting to "Publish content to your Wall". How do I disable this (for fear of scaring off users), as I don't need this enabled. Possible?
Getting the user to authorize one of the extended permissions for your app is kind of the point behind authenticating. You may want to see if the data you need is exposed without authenticating (if you in fact need data). If you can get the uid you can see what is exposed to everyone, and per the privacy changes, I mean everyone. Until the privacy "fire", there was quite a bit exposed to everyone, now 'everyone' means the least restrictive tier among multiple privacy tiers rather than the happy-go-lucky default.
So really the question is, what permission are you trying to get?
There are a few unknown variables here such as what type of app you are making etc.
Check your code for one of the extended permissions, you are probably sending one of them such as offline_access in your auth call.