Using the product warranty registration webpage, users who purchase my product register for a warranty. The data entered by the users are written into Firestore.
However, while registering for a warranty they don't have to login or authenticate.
Is there any way to enforce security rules such a way that users entering data only on my webpage are allowed? (CORS header based etc)
The Firebase Rules do not really care about CORS or any headers in your request. The ideal here would be to enforce authentication to make your data secure, in fact if you check this documentation, open access is considered as a not insecure Firebase Rule.
If this is not an option for you, you can try setting up a middle man that handle this specific request to Firestore that can handle CORS and request headers with some logic and that makes the actual transaction with the Firestore, for this purpose I would recommend creating a HTTP Cloud Function.
That way you can add some logic behind your request handling and actually close the access to Firestore for external users with a retrict Firebase Rule, since Cloud Functions won't be subject to those. This will however create some extra costs to your project because of the use of a Cloud Function.
Related
I am setting up my app so that any client device can only impact collections/sub-collections that they own. However, to interact with other users, a user will need to make the app create a row in another user's collection. What is the safest way to do this?
My idea for this would be to have the app call a cloud function to create the record in the other user's collection. The cloud function would read the request and make sure of the following:
The incoming request has an existing UID
The incoming request's user's email is verified
The incoming request's UID has a record in the Firestore 'users' collection
If I do this, is this just as secure as using Firestore security rules?
The question you're asking is unfortunately not as easy to answer as you expect. Firestore security rules don't ensure general "security" of your app any more than backend code. Rules let you specify rules for reads and writes according to the conditions you provide, if you want to use them. If rules are not sufficient for the requirements at hand, then maybe backend code will work better. In either case, you can allow or deny access based on conditions you provide.
In terms of functionality, both options allow you to allow or restrict access in different ways. Neither one is more or less "secure" than the other. The main issue you should consider is which one lets you most easily specify those rules. Security rules are fundamentally more limited in what you can check, while backend code is fundamentally more flexible. The option you choose is dependent on what you're trying to allow or reject.
The constraints you specified in the question could be enforced by either security rules or backend code, so I don't see that one is necessarily more or less secure than the other.
Is there any way to do some kind of authorization that allows only people who recently requested a page from Firebase Hosting to be able to send an HTTP POST request to a Firestore db and have it go through?
My page is basically an HTML form that posts data to a Firestore page, though it would be nice if at least one had to speak with the server beforehand, as people do not have to log in to post information.
[EDIT]
Requirements:
Serve a static HTML form (with some Javascript included)
The contents of the HTML form should be posted to a firestore database only if the client actually requested a page from the server within a reasonable timeframe
In general, some external code that did not recently request the page should not be able to post data to the database. This is just a minor restriction to mitigate any "attacks" being too easy.
No concept of user or login
All requests should be done through REST as including the Firebase SDK is way too large for this small of a project
With Firebase Hosting, there is no out-of-the-box logging mechanism that would allow detecting if a user has previously requested a page. You need a more "sophisticated" approach.
I can see two possible approaches. (There might be other ones!)
Approach #1 Use two Cloud Functions to:
Serve the page via Firebase Hosting, see Serve dynamic content and host microservices with Cloud Functions
Write to Firestore, after you have verified the user has previously requested a page.
More details:
For the first part, you will not actually serve dynamic content (I understand you plan to serve static pages through Hosting), but because this page is served through a Cloud Function, you will be able to save a unique token (e.g. a Firestore doc ID or any other UUID value) in, for example, Firestore, before sending back the page content.
Then, for the second part (writing to Firestore), the Cloud Function will first check that there is a document with the doc ID previously generated in the Firestore database, and if it is the case, will allow the write to the database (from the Cloud Function).
So, in this case, both Cloud Functions need to be HTTPS ones. You may be interested by this article which details the drawbacks of writing to Firestore through a CF.
Approach #2 Use Firestore security rules for the check before writing.
Do the same than the previous solution for serving the static pages;
Write directly to Firestore and implement a security rule that checks for the existence of a Firestore document with the doc ID saved in point 1. See the exists method.
I've developed an app which relies on Firestore for storing some user's data. This app doesn't have any login mechanism, as it uses the device UUID as identifier; we're not managing any sensitive data, btw.
I'm getting daily warnings from Firestore regarding the absence of security rules in my database, but as long as I don't have any login mechanism and my users need to both read and write from it, I can't see any way for implementing a useful security rule.
Is there any pattern I could follow in this situation? Is there any way to create a security rule for allowing to only read and write data created by the same user without any user authentication?
Thanks in advance
It sounds like you want to identify the user, but then without authentication. My guess is that you want to identify them, without requiring them to provide credentials.
If that is the case, you're looking for Firebase's anonymous authentication provider, which assigns a unique, unspoofable ID to each app instance. Signing in anonymously takes very little code, for example for Android it's:
FirebaseAuth.getInstance().signInAnonymously();
After this call completes, the user has an ID that you can then use in your security rules to identify the data from this user.
I already use ReCAPTCHA for Android apps client-side (I've also implemented, of course, its server-side verification).
However, this ReCAPTCHA is implemented only in one activity. But, of course, hackers can modify the app. For example:
they can simply remove ReCAPTCHA from all activities,
or start another activity that would not have ReCAPTCHA implemented; it's the case btw: I didn't implement ReCAPTCHA in each activity because it's useless according to the first problem I've just mentioned.
So I would want to detect bot and spam requests in Cloud Functions, then in Cloud Firestore, then in Cloud Storage, for the following accesses: read, write, function call. It'd allow me to prevent unwanted contents from being saved in Firestore for example (spamming messages, etc.), and to avoid overreaching my monthly billing quota (because of spam requests to Firestore for example).
Is it possible? How?
There is no "spam detection" for these products. Your security rules will determine who can access what data. If you don't have security rules in place, and allow public access, then anyone will be able to get that data, and you will be charged for it when that happens. This is the nature of publicly accessible cloud services.
If you want more control over the data in these products, you could stop all direct public access with security rules, and force clients to go through a backend you control. The backend could try to apply some logic to determine if it's "spam", by whatever criteria you determine. There is no simple algorithm for this - you will need to define what "spam" means, and reject the request if it meets you criteria.
Google does have some amount of abuse detection for its cloud products, but it will likely take a lot of abuse to trigger an alert. If you suspect abusive behavior, be sure to collect information and send that to Firebase support for assistance.
Just thought I'd add that there is another way to restrict access to Cloud Functions.
Doug already described Way 1, where you write the access logic within the cloud function. In that case, the function still gets invoked, but which code path is taken is up to your logic.
Way 2 is that you can set a function to be "private" so that it can't be invoked except by registered users (you decide on permissions). In this case, unauthenticated requests are denied and the function is not invoked at all.
Way 2 works because every Firebase project is also a Google Cloud Platform project, and GCP offers this functionality. Here are the relevant references to (a) Configuring functions as public/private, and then (b) authenticating end-users to your functions.
When working with Firebase (Firebase cloud function in this case), we have to pay for every byte of bandwidth.
So, i wonder how can we deal with case that someone who somehow find out our endpoint then continuous request intentionally (by a script or tool)?
I did some search on the internet but don't see anything can help.
Except for this one but not really useful.
Since you didn't specify which type of request, I'm going to assume that you mean http(s)-triggers on firebase cloud functions.
There are multiple limiters you can put in place to 'reduce' the bandwidth consumed by the request. I'll write a few that comes to my mind
1) Limit the type of requests
If all you need is GET and say for example you don't need PUT you can start off by returning a 403 for those, before you go any further in your cloud function.
if (req.method === 'PUT') { res.status(403).send('Forbidden!'); }
2) Authenticate if you can
Follow Google's example here and allow only authorized users to use your https endpoints. You can simply achieve this by verifying tokens like this SOF answer to this question.
3) Check for origin
You can try checking for the origin of the request before going any further in your cloud function. If I recall correctly, cloud functions give you full access to the HTTP Request/Response objects so you can set the appropriate CORS headers and respond to pre-flight OPTIONS requests.
Experimental Idea 1
You can hypothetically put your functions behind a load balancer / firewall, and relay-trigger them. It would more or less defeat the purpose of cloud functions' scalable nature, but if a form of DoS is a bigger concern for you than scalability, then you could try creating an app engine relay, put it behind a load balancer / firewall and handle the security at that layer.
Experimental Idea 2
You can try using DNS level attack-prevention solutions to your problem by putting something like cloudflare in between. Use a CNAME, and Cloudflare Page Rules to map URLs to your cloud functions. This could hypothetically absorb the impact. Like this :
*function1.mydomain.com/* -> https://us-central1-etc-etc-etc.cloudfunctions.net/function1/$2
Now if you go to
http://function1.mydomain.com/?something=awesome
you can even pass the URL params to your functions. A tactic which I've read about in this medium article during the summer when I needed something similar.
Finally
In an attempt to make the questions on SOF more linked, and help everyone find answers, here's another question I found that's similar in nature. Linking here so that others can find it as well.
Returning a 403 or empty body on non supported methods will not do much for you. Yes you will have less bandwidth wasted but firebase will still bill you for the request, the attacker could just send millions of requests and you still will lose money.
Also authentication is not a solution to this problem. First of all any auth process (create token, verify/validate token) is costly, and again firebase has thought of this and will bill you based on the time it takes for the function to return a response. You cannot afford to use auth to prevent continuous requests.
Plus, a smart attacker would not just go for a req which returns 403. What stops the attacker from hitting the login endpoint a millions times?? And if he provides correct credentials (which he would do if he was smart) you will waste bandwidth by returning a token each time, also if you are re-generating tokens you would waste time on each request which would further hurt your bill.
The idea here is to block this attacker completely (before going to your api functions).
What I would do is use cloudflare to proxy my endpoints, and in my api I would define a max_req_limit_per_ip and a time_frame, save each request ip on the db and on each req check if the ip did go over the limit for that given time frame, if so you just use cloudflare api to block that ip at the firewall.
Tip:
max_req_limit_per_ip and a time_frame can be custom for different requests.
For example:
an ip can hit a 403 10 times in 1 hour
an ip can hit the login successfully 5 times in 20 minutes
an ip can hit the login unsuccessfully 5 times in 1 hour
There is a solution for this problem where you can verify the https endpoint.
Only users who pass a valid Firebase ID token as a Bearer token in the Authorization header of the HTTP request or in a __session cookie are authorized to use the function.
Checking the ID token is done with an ExpressJs middleware that also passes the decoded ID token in the Express request object.
Check this sample code from firebase.
Putting access-control logic in your function is standard practice for Firebase, BUT the function still has to be invoked to access that logic.
If you don't want your function to fire at all except for authenticated users, you can take advantage of the fact that every Firebase Project is also a Google Cloud Project -- and GCP allows for "private" functions.
You can set project-wide or per-function permissions outside the function(s), so that only authenticated users can cause the function to fire, even if they try to hit the endpoint.
Here's documentation on setting permissions and authenticating users. Note that, as of writing, I believe using this method requires users to use a Google account to authenticate.