Our app allows authenticated users to send and receive digital coins from each other in the app. Each user has a coins field which stores the current coin balance of a user. To transfer coins, we use firestore transactions at the sender's end to subtract the value of coins from the sender and then add the value to the receiver's balance. Hence, while the sender is logged in, they have to make writes to the receiver's document. This approach is insecure since we only check if the sender is logged in. This enables any authenticated user to have write access to another user's document.
Is there a much better/secure approach to implement such kind of transactions between users and is it possible to handle the problem only using firestore security rules in this situation?
In this scenario, you wouldn't want to give users write access to each other user's documents, or even their own "wallets". Doing this type of transaction all in the client side (e.g. using Firestore iOS/Android/Web SDK) will likely cause security issues down in the road. Of course, not many users will be aware of it, but tech savvy users who finds out that they have write access to other users' Firestore documents as long as they are signed in, will start deducting and taking coins from other users' wallets.
You will probably need to setup a server, or a script which will use Firebase Admin API to run transaction that you are talking about, so that it will be done in a black box according to your users' point of view.
Once you have the server up and running, client side app will then need to make calls to the server, preferably with auth tokens (e.g. Firebase Auth User's token), and ask for the transaction to run.
Related
My team is using Firebase Auth in our project. We're using custom claims to properly authenticate whether or not a user has access to a specific piece of information. We're storing each piece of information and deciding access for the user based off whether or not the user has access to a specific location. Each piece of data is somehow associated with a location, and we are storing the location id in the custom claims for the ID Token.
The problem is that a user potentially has access to thousands of locations. Instead of storing thousands of locations in the custom claims, we think it's better to generate a new custom token and exchange it for a fresh id token every time the user switches locations. That means that it's potentially reasonable for a user to generate a new custom token every few seconds as they switch through their locations if they are doing it quickly enough.
The Firebase Auth documentation doesn't indicate anything in regards to any rate limiting in this regard.
Are we going to run into any issues / rate limiting if a user switches through their locations very quickly? This would be unusual, but I just want to make sure that the worst case will still work.
The control of Firebase Authentication custom claims is an Admin SDK operation and as such is not subject to rate-limiting. Firebase Authentication as a whole is designed to handle millions of concurrent users and is subject to various rate limits. Updating a user's custom claims would only contribute to the overall limits of 1000ops/sec and 10 million ops/day (at time of writing).
However, custom claims should be used for access control related data that will infrequently change such as the name of a role or a building ID. This is because the propagation of changed custom claims can take up to an hour and requires that the client code manually refresh their tokens after any changes to see an immediate effect. Custom claims have no mechanism to notify clients that they have been updated.
I'd reconsider your approach and store the list of locations a particular user has access to in the Realtime Database or Cloud Firestore rather than the ID token itself. You can then use this information to secure their access using appropriate Security Rules for those databases based on their user ID. By configuring a listener to that portion of the database, your client code can be instantly notified when a user gains or loses access to a new location. Additionally, you can poll information from multiple locations at once so you can show information in a notification feed or dashboard - something that can't be done with the claims strategy.
I am confused on the insistence in the documentation to have users sign on to give authentication. With a card game app, I want users to get on easily (without a need to sign on), find a game to play in, and play the game. As cards are played, each turn is recorded in Firestore, and all users notified to read the changed parameters. So all users can read, write and update.
Even if I did make users sign on, anyone could create an account, and I would be no further ahead except I have turned away some game participants.
I know I can set rules so that all data is a small size. I maybe check to ensure no data is stored after a certain time without being used, and I could delete the collection for that game. Is is it this sort of procedures I use to guard the database from growing too big, or a 'bad' user working out where the data is, and overwriting data for some games.
If you want a low-friction way to get users signed in, use anonymous authentication. It will give the user an account with a UID without requiring any prompts. You can then use security rules to protect per-user information, since anonymous accounts have a UID just like normal accounts. You can then decide to let the user upgrade that account when they want a more permanent identity in your game (since anonymous accounts become inaccessible if the user clears their device local storage).
When we are talking about individual private push notifications per user (rather than per device), there are couple of ways to send them via Firebase:
Collect registration tokens, associate them with the user on the trusted server and send notifications to all registered tokens.
Assign a topic per user, e.g. topics/user-id and let the app subscribe to it upon successful login / unsubscribe on logout.
However, Firebase documentation recommends to use approach no. 1, saying "For fast, secure delivery to single devices or small groups of devices, target messages to registration tokens, not topics."
One concern is the latency (referring to "fast") issue. Is it really that significant in practice comparing registration tokens vs topics-based messaging?
More serious concern is security.
What does Firebase documentation mean exactly by "... secure delivery..." as opposed to topics?
In our case one device will likely be used by multiple users logging in and out. That means registration tokens will have to be disassociated from the user in the backend when user logs out. If something goes wrong and registration token is not disassociated, device will continue getting previous person's notifications.
Is this the trade-off I have to accept or am I missing some other option?
There are a few questions in there. Let's see if I can cover them main ones.
If you need to deliver messages to multiple tokens, the two approaches you're considering are:
Subscribe the app installs/tokens to a specific topic, that you then deliver the message to.
Keep your own registry of tokens for the user, and do the fan-out of user-to-tokens in your own code.
The biggest difference is in where the fan-out of a user to their tokens happens. When you use topics it is done by Firebase on Google's servers, while in the second case you do it yourself. There is no guaranteed performance difference between these two, but in the latter case you have more control. So you spend more effort (writing your own code for something Firebase can do for you), and in turn gain more control (which may or may not translate into better performance)..
The second question is around the security of topics. The documentation contains that note because topics often have a much simpler structure than tokens. For example, if you have a topic-per-user, you will often use the UID as the topic ID. And since you may be sharing that UID in other places, it is possible that other users may know a user's UID. And since subscribing to a topic only requires that you know your own token and the topic ID, that means that any user can subscribe to another user's topic.
In my Android application I have an idea to store in database some serial key. If user enters correct key he gets full version of application and the key is disabled on the server to avoid multiply usage of the same key, otherwise he can buy app in Google Play without a key.
For this I thought to use Firebase Database but after read this I have some doubts
Firebase Realtime Database
Store and sync data with our NoSQL cloud database. Data is synced across all clients in realtime, and remains available when your app goes offline.
Does it mean that firebase will duplicate the table with all available keys to all application users and some smart user can read the list from this copy at his phone?
Not all data is automatically duplicated to all clients. Only data that the client subscribes to is received by that client.
You can control what data each client can see through Firebase's server-side security rules. For example, you'll typically want to ensure that each user can only read their own data.
It probably isn't a good idea to store super-sensitive data like social security numbers or credit card numbers, but if you see https://firebase.google.com/docs/database/security/ you can see, that you can control access to data, & use validation, especially since you can regenerate the keys if they become compromised, it wouldn't be the worst option. If you look at https://firebase.google.com/docs/database/security/user-security you can see, that it's possible to write an app that uses it like google drive with a smartphone-based client.
Personally the answer would no. You may want to think about Google Play Subscriptions and In-App Purchases.
If you really have to then:
Create a key as a user buys the upgrade (server-side).
Store the device id/account id (hashed) and timestamp with the key.
Credit card details and expiry dates should be combined into one hash.
Just encrypt everything.
It's better to have a banned list than a list of approved key. Eventually you have to create more keys and it's easier just to maintain a list of banned keys.
I am developing a website that has a booking option. Users can look at the availability on my calendar and pay for a timeslot.
Here is my concern: Let's suppose the website uses PayPal, and I get verification for when the payment goes through... then the website will populate the database with the clients booking information and close that time slot on the schedule. Now, what if I were to just send information to the database. Suppose I modify the code on the client side and just send booking info to the database. Nothing is stored in the database to authenticate this update and so an appointment could be booked without payment. I believe this is possible and so I was wondering how I could secure it as to prevent this from happening.
I know this is a long question, but I really hope you understand what I am asking. If you have any suggestions please let me know!
Any security should always be done on the server. In the case where you have the client code write directly to the Firebase Realtime Database, you'll rely on security rules.
Typically you'll write a record about each verified payment into the database. E.g.
payments
user1: 15
user5: 25
The user1 and user5 keys above are the IDs of the users whose payment you recorded. Your users get this ID from Firebase Authentication.
Note that this write operation should be performed by a trusted our, so typically you yourself, a script that runs on a server you control, or on Cloud Functions.
Then when a user tries to write a booking to the database (say in /bookings/book123) you only allow the write if there is a payment from that user:
{
"rules": {
"bookings": {
"$bookingid": {
".write": "root.child('payments').child(auth.uid).val() > 10"
}
}
}
}
The auth.uid is the ID of the user performing the write. Firebase ensures that this is the actual user, and cannot be spoofed by someone (unless they know your Firebase Authentication private keys). The rules above this only allow a write from a user if they have a payment (or of more than 10).
Note that this is just a simple example. You may want to instead track payment/transaction IDs (from Paypal), and use those to synchronize up with writing of a booking with the receipt of a payment.