in the last few days I have looked into Google Cloud Storage Buckets. I would like to know how to authenticate and authorize users when accessing data, preferably without the use of a backend.
Context: I have an app with the following requirements: Authenticated end users should be able to upload data to a (or their) bucket, with the default read access being scope to the user. At any point, the owner of the bucket should allow the bucket contents to be available to the public (publish bucket contents, read only).
End users are currently being authenticated with JWTs on the browser.
I have looked at the different ways of controlling access to Storage Buckets.
To my understanding:
IAM is unsuitable as it is meant for Google Accounts and should be used within the company, not to authenticate end users (clients)
ACLs are seemingly not recommended and are described as a legacy way meant for interoperability with S3
Signed URLs are "ok" for uploads, but I would rather have an actually authenticated way of uploading.
What's totally unclear to me: what access control method can be used to authenticate my end users (pref. with JWTs) especially for reading data?
This seems like an issue everyone should face, but I can't seem to find good info? On a side note: I am aware that Firebase exists for this reason, I just want to know how to tackle this on GCP.
There is no other solution than signed URL and a backend (I know that breaks your requirement) that check the authentication and generate that signedURL (on only the relevant/authorized files)
When implementing this purely in GCP, you'll typically end up implementing your own auth solution for your clients, and then your own authorization model in your server-side code.
If you want to not implement this yourself, using Firebase for your Cloud Storage access would be the way to go. This implements client-side authentication and server-side security rules to control access.
Related
Disclaimer: I am new to mobile app development and have little to no knowledge on authentication systems
Normally, when my mobile app makes https calls to my backend server, I know that I cannot trust that these calls to my server came from my app, as anyone can make https requests to my backend server. Even if I give the app a secret key, it is still possible for a hacker to obtain the key and include it in https requests. Therefore, I will not allow https requests to accomplish whatever it wants on the server; rather, I will limit the request to doing only what a user can normally do with their own data – delete their OWN posts, edit their OWN profile, and so on.
Does Firebase work the same way? I saw this StackOverflow thread regarding OAuth consumer secrets, and how they can be compromised and used to imitate a mobile app.
Is this also the case for Firebase?
Can a malicious user theoretically obtain whatever keys/secrets Firebase gave to my mobile app, and use that to emulate requests from my app to Firebase? For example, could they create new users and cause de-syncing issues with my own backend database?
If so, how can I prevent it?
Thanks.
Does Firebase work the same way?
Firebase works in whatever way you program it. Normally you do not put private keys in software that you distribute to end users. The recommended approach is documented very well - use Firebase Auth ID tokens to indicate who is making the call, and use code on your backend to figure out if they should be able to do the work they are requesting. This is what happens with direct database access from your app, but you have to write security rules to protect data according to your requirements.
If you are passing tokens yourself to your own backend, it is up to you to revoke any refresh tokens that you find to be compromised. You cannot fully stop hackers from compromising a system that stores user tokens on devices that you don't control. All you can do is make it hard for them to do so.
Can a malicious user theoretically obtain whatever keys/secrets Firebase gave to my mobile app
Yes, that's why you don't put secrets in code that you distribute to end users. The Firebase config that you're asked to add to your app is not considered private.
See also:
Is it safe to expose Firebase apiKey to the public?
My application has Firebase users (i.e. users created in Firebase Authentication, NOT in Firebase IAM or in GCP IAM). These users are not linked to a G Mail or Google Workspaces (formerly G Suite) account, and are not part of my organization.
I need to grant each of these users write access (not read) to a Cloud Storage bucket (1 user = 1 bucket), while not allowing any kind of access to that bucket to unauthenticated users or to other Firebase users.
How would I go about doing that?
I have tried verifying auth and generating a presigned URL from my Cloud Functions backend, but it has turned out a bit problematic with uploading thousands of files, which is why I'm looking at alternatives.
Time-limited access is not a requirement for me either way (I'm fine with users only having a few hours of access or having forever access). Also, if one bucket per user is too problematic, one folder per user, all inside the same bucket, would also be acceptable.
I know that in AWS I could use Cognito User Pools for the users, and then link the users to an Identity Pool so they can obtain temporary AWS credentials with the required scope, but I haven't been able to find the equivalent in GCP. The service comparison table hasn't helped in this regard.
I realize I might have the wrong idea in my head, coming from AWS. I don't mind if I have to link my Firebase users to GCP IAM users or to Firebase IAM users for this, though to me it sounds counter-intuitive, and I haven't found any info on that either. Maybe I don't even need GCP credentials, but I haven't found a way to do this with a bucket ACL either. I'm open to anything.
Since your users are signed in with Firebase Authentication, the best way to control their access is through security rules that sit in front of the files in your storage bucket when you access them through the Firebase SDK.
Some example of common access patterns are only allowing the owner of a file to access it or attribute or role based access control.
When implementing security rules, keep in mind that download URLs that you can generate through the Firebase SDK (if have read access to a file) provide public read-only access to the file too. These download URLs bypass the rules, so you should only generate them for files that you want to be publicly access to anyone with that URL.
As I understand firebase security rules are for authenticating different types of users and provide authorization based on that, but what if my application doesn't need users to register at all? What if I just need to authenticate the application, not the users? What I mean by that is, I need to assure that particular firebase products are only accessed through an given application.
What I currently do to achieve that is, just make the security rules public assuming that the specific firebase sdk does the authentication stuff, but when I do that, I get the weird warning from the firebase console that security rules shouldn't be public. What am I doing wrong?
Another question, shouldn't we authenticate any application trying to access the firebase products even before starting the user authentication?
I also would like to learn how this is done generally (best practices) when developing serverless applications with firebase/google cloud platform products.
Firebase security rules don't support authenticating apps, only users. Authentication of an app would be pretty easy to simulate by an attacker, as all you would need is the secret compiled into the app in order to fool the rule. Even if you obfuscate the secret data, it's still just public data, and someone will figure out how to use it.
Once you ship an app to the world, you should consider everything in it to be public information, no matter how hard you might think it would be to extract that information (it's not that hard, really).
Can somebody else get the Firebase credentials from my APK and use them? Is this prevented by adding the SHA-1 keys for Android?
If it is prevented, what do I need security rules for since only code from my app with my SHA-1 can manipulate database at all?
If it is not prevented, can somebody else use my Firebase database as long as his requests fit the security rules? (Write 2nd client, which actually cannot do bad things but should not be allowed at all.)
Im not sure how I should think about security rules:
A) Protecting data against access and manipulation from bad guys + B?
B) Just a set of rules to keep data in a certain state and prevent my software from doing invalid database request?
A Firebase Database can be accessed via the REST API, or any of the client libraries. The decision about whether a client can or can't do something is entirely based on the rules.
You can even just access the Database URL in a web browser and see a JSON response by putting .json on the end, e.g. https://[YOUR_PROJECT_ID].firebaseio.com/.json
So the answer is definitely B! The default rules in a new Firebase project are that read and write to the database require auth, but you can configure them to provide whatever levels of protection you need.
Take a look at the Database Rules quickstart to see what you can do!
We don't ship the Realtime Database secret (or any other "secret" material) in the json file that gets baked into your app. That file simply contains resource identifiers that allow us to know which resources (database, storage bucket, analytics, etc.) to properly authenticate to (we use Firebase Authentication for these purposes), and we handle server side authorization to ensure that users are properly logged in.
If you are authorizing your requests properly (using Firebase Realtime Database Rules, for instance), your data is secure!
I'd recommend watching The Key to Firebase Security, one of our I/O talks, which talks in greater detail about how this works.
firebaser here
Thanks to the new feature called Firebase App Check, it is now actually possible to limit calls to your Realtime Database to only those coming from iOS, Android and Web apps that are registered in your Firebase project.
You'll typically want to combine this with the user authentication based security that Mike and Ian describe in their answers, so that you have another shield against abusive users that do use your app.
I'm looking into using Firebase for a project that's otherwise a static site with js. Is it possible to secure the connection to the database in a static site? Wouldn't all security rules, the end point url, etc all be exposed in the js code letting the user make any requests they want (good or bad).
The short answer is yes: by authenticating your users and writing security rules, you can fully restrict read / write access to your Firebase data.
In a nutshell, Firebase security is enforced by server-side rules, that you author, and govern read or write access to given paths in your Firebase data tree.
Firebase security rules are JavaScript-like expressions: easy-to-write expressions that have access to the credentials for the connection, and the view of the Firebase data tree as it exists, along with pending changes on write.
In most cases, your client-side logic, templates, assets, etc. will be static and public. What you're really looking to secure is user and application data, and this is where Firebase Authentication (whether using custom Firebase authentication tokens or Firebase Simple Login) comes in. Firebase Authentication is essentially token generation - taking confirmed, identifiable user data and passing it securely to Firebase so that it cannot be spoofed. This confirmed credential data is then made available in your security rules.
Check out https://stackoverflow.com/a/20842986/879295 for an example, and the Firebase Security Quickstart Video for a great overview / starting point.