Securing a web page and required resources with Google Cloud Storage and signed urls? - google-cloud-storage

I am trying to secure access to premium content on my app engine web application. The content is an Articulate module, it is a player.html with dozens of associated javascript, audio, and video resources. I am trying to use Google Cloud Storage to host the content.
My idea is that when a user who is authenticated with my app and has appropriate access requests the premium content, my app could sign a url to the player.html. What I am not sure about is how to handle all the associated resource files? Signed urls are simple enough for securing single files, but what about groups of files? Do I have to sign urls for all the content or is it possible to have a single signed url allow access to related files?
ACLs are not an option, because I rolled my own authentication rather than using oAuth and Google accounts.
Any thoughts or alternate strategies would be appreciated.
Update 8.7.13
After reflecting and researching some more, I am wondering about configuring the GCS bucket as a website as per the instructions here.
If I understand the docs correctly, I would create a CNAME to point requests from my custom domain content.example.com to c.storage.googleapis.com, and request that arrive via this CNAME are served up as if they were a static webpage. Does anybody know what access controls are available (if any) to files served in this manner? Would files served this way also require signing / ACLs if they are not public?

Unfortunately, your options here are somewhat limited. Below are two ideas, but both of them have fairly significant drawbacks. Neither's super great, unfortunately.
Obfuscation
Make all associated resources publicly accessible and kept under an unguessable subdirectory. The user would use a signed URL to load the secured root resource, which would include links to the publicly accessible secondary resources. Downside: the sub-resources are not secured and could be downloaded by any party that knows the URL. Possible mitigation: periodically rotate sub-resource directory.
Appengine Redirects
Change includes to route all resources through your appengine app, which would authenticate using its own scheme and redirect to a new signed URL for each subresource. Downside: lots of extra hops through appengine. So for example, player.html would include a movie as "https://yourapp.appspot.com/resources/movie.mov", which would hit your appengine app, which would in turn authenticate then redirect to the signed "https://storage.googleapis.com/yourbucket/movie.mov?signaturestuff"

Related

How do I specify PWA Storage URL?

I have two PWAs in individual repositories on my GitHub account (e.g., https://github.com/1John419/pwa01 and https://github.com/1John419/pwa02). The apps are installed from their respective GitHub Pages (e.g., https://1john419.github.io/pwa01/ and https://1john419.github.io/pwa02/).
The problem is the Local Storage and Cache Storage for both apps are pointing to the domain URL (https://1john419.github.io/) rather than the app URL (https://1john419.github.io/pwa01 and https://1john419.github.io/pwa01).
As a result, even though each app's sw.js is only caching its respecting data, DevTools is indicating that each app contains all caches from the domain URL. The service workers are pointing to the app URL, but the Local Storage and Cache Storage are pointing to the domain URL.
When either app is updated, files with common names appear to be overwritten (despite being in uniquely name caches).
Is there a way to make the storage URL point to the app URL rather than the domain URL? If not, what solution would you suggest to keep the apps caches separate?
Update: PWA repos have now been deleted.
This is the solution I found. If you know of a better way, post another answer.
In short: No there is no way to specify PWA Storage URL. Browsers use the FQDN as the storage URL.
In order to have a unique storage URL for each app, each app needs to be in its own domain or subdomain.
The best solution I found was to purchase a domain and setup a subdomain for each app. I found these resources very useful:
Mike Conrad
Trent Yang
GitHub Docs
Using this approach, each app has its own storage URL and the apps don't step on each other any more.

Google storage external authorization

I need to store my service data in Google Storage and let my users download files depending on their (users) access rights.
I've already made service that connects to Google Storage using server-centric mechanism, and transfers them to client-side, but I need client-side to go to Storage and download file without server-side.
I've tried to use temporary links for files, but I can't check, if user downloaded file or not to properly delete temporary link.
I've tried to look for oauth2 support, but it seems Google doesn't support oauth in such way (When my service decides to allow access or no).
The best solution is to generate tokens for users and if Google Storage would call my service before every file download.
How can I achieve that?

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.