Using Firebase Security rules, how can I check for available usernames without making the entire node public? - swift

In my Firebase database, I have a section for storing usernames that are taken.
There is a “usernames” node, where the username is the key, and the user’s ID is stored in a “userId” atrribute.
usernames
{
username1
userId : "exampleId1"
username2
userId : "exampleId2"
username3
userId : "exampleId3"
...
}
When a user is signing up, before they create an account and are Authenticated, the app must check that the username is not taken.
In order for this to work, the “usernames” node has been set to public in the Firebase Security Rules:
"usernames": {
".read": true
}
Unfortunately, this will make every taken username and internal user ID visible, which is a security concern and not something that should be done.
(for those that don’t know, public nodes can be accessed through a browser like so):
https://mydatabasename.firebaseio.com/usernames.json
There are other nodes for banned usernames and emails that work in a similar way; they have to be checked before a user is Authenticated, and should not be fully exposed to the public.
My question is: When a user is signing up, how can I check for available usernames without making the entire node public?

To know if a specific user name is already taken, the user doesn't need read permission to /usernames but it's suffice to give them read access to /usernames/$username. So:
"usernames": {
"$username": {
".read": true
}
}
With these rules, you code can check whether the specific user name that the user wants to claim is already taken (by someone else), but they can't request a list of all user names.

Two options comes to mind, the first is allowing the public read access to your database while the second method is what I would do in a real project:
Method 1: Maintain a Separate "Usernames" Node
With this method you create a secondary node, let's say it's called usernamesInUse and this would be world readable. Its structure would look like this:
{
"usernamesInUse": {
"username1": true,
"username2": true,
"username3": true
}
}
Checking if a username exists is as simple as:
db().ref('usernamesInUse/username2').once('value', (snapshot) => if (snapshot.exists()) ...)
The downsides to this method are that you have to have processes in place to update this node whenever a new user is added, modified or deleted. However this would give secure read access to usernames and nothing else.
Method 2: Create a Cloud Function (How I would do it)
Create a simple Cloud Function with an HTTPS endpoint that checks for the existence of the username and returns a 200 or 404 status code. Your database would not need any world readable permissions.
This avoids the need to duplicate data, prevents users from downloading a full list of every user in your system and prevents the world from unmetered access to your database. You also have the opportunity to block access to abusive anonymous visitors.

Tell me if you like the idea. :)
I would create a singleton with all func and variable private which will only return a Bool and take Username variable.
This way no one can access data or func from this part. No injections possible.
Insisde the singleton you can check all usernames and do whatever you want.
return only Bool.

Related

How do you know if your firebase security rules are good enough so you can't get hacked?

I am brand new to firebase and i'm not sure if my firebase security rules are good enough so no one can hack into my database and change whatever they want. How do you know if your firebase security rules are any good?
I really do not want to allow anyone to change any data unless they are the current user changing only their personal data.
Here is a picture of my current firebase security rules:
How do I know if my firebase security rules are any good?
Also not sure if this will be useful but I only have 2 collections in my firestore data. The first one is "users" that just has basic information about my users (name, email address, etc). The second is "posts" that just has basic info about a post (likes, comments, etc)
How do you know if your firebase security rules are good enough so you can't get hacked?
That is really unanswerable. It's like asking "How do I know I'm the smartest person in the world"; you can't know that without competing with every person in the world in a battle of the brains.
Your security rules are a bit like that, except that you're not competing with everyone else but only with folks trying to access your data in a malicious way. And instead of an undefined battle of the brains, you're pitching your security rules (which they can't see) against their skills of deduction and familiarity with the API.
Good security rules allow exactly what your code also does, and nothing else.
A good example of this is your first create rule:
match /users/{userID} {
allow create: if true;
}
So this rule allows anyone in the world to create a document under any ID that they want in your database, simply by calling firebase.firestore().document("users/IAMTREV347").set({ whateverKeyIWant: "WithWhateverValue" }).
Your own code is probably be a bit more restrained than that. For example, it seems that you want to store documents in /users/$uid, so under the UID of the current user in your app. In code that might be something like this:
const uid = firebase.auth().currentUser.uid;
firebase.firestore().collection("users").doc(uid).set({
uid: uid,
name: "Trev345"
})
So you'll need to tighten your code to:
Only allow the user to write to the document with their own UID both as the document ID and in the uid field.
Only allow the user to write the name field in that document.
If you modify your rules like that, they allow exactly what the code does and nothing else, so there is no room for anyone to abuse them.
So the proper create rule would be:
match /users/{userID} {
allow create: if request.auth.uid == $userID &&
request.document.data.uid == $userID &&
(request.resource.data.keys().hasOnly(['name', 'uid']));
}
You should go through each of your rules like that and through each piece of code that accesses the database, and give the minimum permission that allows the code to work.

Allow unregistered users to query Firestore to check if an E-Mail already exists

Right now, in my Firestore rules only registered users have read / write access to my project. However, I also want to verify in my registration process if an E-Mail already exists, which means also anonymous user have access to my "users" data.
I am unsure how this is compliant from a security perspective. Should I create something like a "emails" collection and duplicate all E-Mail addresses here and allow anonymous users to query this?
Yup, that is indeed the typical approach. Duplicate the data that you want anonymously accessible into a separate collection, and grant broader access restrictions there.
In the rules for that collection of email addresses you will typically use granular rules that allow reading each specific document, but to disallow listing all documents. Something like:
match /emails/{email} {
// Applies to single document read requests
allow get: if <condition>;
// Applies to queries and collection read requests
allow list: if false;
}
This means that once the user has typed a specific email address, you can check if a document exists for that email address. But they can't just get all documents from the collection to scrape your user base.

How to securize an entitie on Sails?

I'm developing an API with Sails, and now I need to securize some variables from an entity. Those variable will be accesed only from Admin or own user.
I have an structure like this:
Employee (contains your employee records)
fullName
hourlyWage
phoneNumber
accountBank
Location (contains a record for each location you operate)
streetAddress
city
state
zipcode
...
I need to encrypt phonenumber and accountbank, to avoid anyone to see the values of this fields in the DataBase. Only the owner or the admin.
How I can do that? Thanks
You are looking for a way to encrypt data so that people with no required access right could not see it.
The solution for that is not Sails.js specific and Node actually comes with tools to encrypt data :https://nodejs.org/api/crypto.html.
The key rule here is to always keep your secret password safe.
As for integration in your Sails.js application, I would use callbacks in Models. The official documentation provides a good example here : http://sailsjs.org/documentation/concepts/models-and-orm/lifecycle-callbacks
Basically you just define a function that will be called each time the record is about to be created, fetched or updated. You can then apply your encrypt/decrypt functions there.
This will encrypt/decrypt your phone numbers and bank account numbers automatically.
Regarding access control, you can use Sails' policies along with authentication to determine if the client has the right to access the resource. If not you can always remove attributes from the response sent back to the client.

REST API logged in user can access data (parse.com)

I'm using the parse REST API.
I need to setup so that for any requests made:
1) only logged in/authenticated users can Read or Write.
2) users can only access/modify records they own.
My current implementation:
1) using the Application key + REST API key.
2) sending request to user login endpoint, on success returning the user data including the session token
for 2), I'm not doing anything with the session token yet.
I understand that parse has:
1) class based permissions
2) object-level permissions (ACL's)
With Read and Write access on the class level, and by simply using the Application Key + REST API Keys,
anyone with these two keys can access that class (ofcourse, the Master Key has even more "power").
I want to simply say that they can Read and Write on the class level, if they're logged in/authenticated.
And when they Read, Update or Delete, they can only do so if they're owner of the object.
I assume that session token will play a role in the logged in part, and ownership is defined by object-level ACL
Is this correct and how to roughly set this scenario up in parse?
It's not clear to me in the REST API how to handle this (what I think is a common) type of scenario.
Thanks for any feedback
{"ACL":{"$CURRENT_USER":{"read":true,"write":true}}}
above in acl column will mean at the security level, only the creator has RW permissions. No other user can see these records with this ACL attr value regardless of their access on the CLASS level.
OR
you control the accessor predicates in your app. So you can add a column = 'createdBY' of type pointer_to_class_User.
Any queries just contain predicate ..
'where={"createdBy":{"__type":"Pointer","className":"User","objectId":"$CURRENT_USER"}}'
which enforces ( outside row security level ) idea of only getting result sets containing rows for the current-user.
all depends on how you want to use the security layer.
I would do it using the predicates and resort to the ACL only where you may have stuff like SSN's or Salary where as a policy you dont what general read permissions.

How to ensure that all WebAPI/OData requests pertain to a specific user?

I found this process simpler when consuming a WCF service (SOAP), but here goes...
Where Users 1..* Categories:
In exposing an OData RESTful service (MVC4, WebAPI, OData), client-side code could be issued like:
/odata/Categories?$filter=startswith(CategoryName,'paid')
Or even
/odata/Categories
Would return all categories for all users. Not very secure.
I want to ensure that all requests issued when a user is logged in (I'm using forms authentication with my own custom role provider) only return data related to that user (where userID=x). Is there a custom filter that needs to be created, whereby even if a logged in user saw the outgoing WebAPI/OData request paths (often defined in JavaScript), they can't try to get other user's information?
Each navigation property or related table in the db has a UserID field where the "AND UserID=x" could be used.
Would this involve creating some sort of FilterQueryValidator (actually query addition) similar to what's found at the bottom of this page?
I'm just not sure on how this works, and would like some pointers.
FYI: This may be related to this other question, but more specific on user-based requests.
You can use a filter attribute or you can override GET / POST etc in your ODataContoller to add the UserID == X to the Linq expression.
e.g.
public override IQueryable<Product> Get()
{
return _context.Products.Where(x => x.UserID == user_id);
}