I am relatively new to web-developing as well as using React.js and wanted to double-check on some security concerns:
Authentication
Assuming part of my app should not be accessible for unauthenticated users and I implement this by calling for example AWS Cognito and a conditional render:
{Auth ? <userPanel/> : <smthElse/>}
Is there any scope for client-side manipulation i.e, is there a way to access the userPanel component despite being rejected by Cognito?
Authorisation
Furthermore, if I have different user groups (user, admin etc), what would be a safe way to restrict different parts of the App? Some options i have come up with so far:
Same as above, with directly calling the Cognito API and a conditional render
Maybe there is way to make the Redux state temper-proof and to use it in combination with private react-router-dom routes
Use private routes in combination with direct calls to the Cognito API
Thanks
Client-side authentication/authorisation is impossible and should be avoided.
The front-end should not be bundled with any sensitive data.
Secure your API endpoints instead, so that even if users edit the source to render the component/page, they will not see any sensitive data rendered unless they are authorised.
Nothing on client side is tamper proof. I think your solution is fine as long as you're performing a back-end validation as well, before rendering the page.
If you're really concerned that this might be changed from the client side, I would suggest setting up an entirely different route for authenticated vs non-authenticated users
Related
I'm a quite new JavaScript developer, coming from the Embedded Systems area, i.e., algorithms and programming basic concepts are not an issue for me, but some concepts of web development (front/back end) are still new to me, and many doubts often come up.
Going directly to my question, I'm currently developing a basic HTML + JS application where the user must login to get access to a "private" area; once logged-in, it is possible to perform many service requests and see other users requests, being more or less an "intranet" system.
I'm using Firebase Firestore as the database, and adopting two approaches to comunicate with the database:
-For user authentication management (log-in and sign-up processes), I'm taking advantage of the "front-end" JS modules provided by Firebase, using the login/password method with the e-mail link confirmation feature activated. Once an auth request is sent, the firebase app returns a token, and based on this token a session cookie is generated, granting access to the private area. This way, I don't need to manage most of the auth / account validation steps in my back-end part.
-For any other database operations (firestore read & write), I'm using the 'firebase-admin' SDK in conjunction with my back-end implementation, in a way that all the requests are validated with the session cookie before being actually performed. The back-end has access to a service account JSON "key", which gives it total read & write access to the whole database.
Finally, the questions are:
-Is it acceptable or conventional to use both approaches, front-end and back-end, to manage the firebase application?
-Once all the firestore data is being managed in the back-end side, and all the read/write/privilege rules are managed there, is it correct the correct approach to set the security rules to block everything, as follows? (considering that firebase-admin bypasses everything from the back-end side, not being affected)
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}
Best regards, and please let me know if I was clear in my doubts :)
Combining front-end and back-end code when using Firestore (or other Firebase modules) is quite normal. Performing all data access through the Admin SDK (while not necessary) is a completely acceptable approach too. Setting the rules to not allow any direct access from client-side SDKs (or with ID tokens through the REST APIs) is indeed a logical step in this scenario.
On thing to realize is that your code is now responsible for ensuring all data is authorized, while in cases where you use client-side Firebase SDKs it is the security rules that perform access control.
I am working on a Meteor application that uses Iron Router. We have a login page in our application. Even when the user is not logged in, if I call the below code on developer console as un-authenticated user:
Router.routes
It gives all the routes and the respective paths. Is there a way to disable access to these paths or do I need to push these end points in the server code ?
Everything you define on the client is visible on the client. So is all the routes definitions and routes logic, too. In production code this will be obfuscated but it's still there.
If you are concerned, that clients can read your routes, then you should be more concerned about the fact that it concerns you. Sounds a bit confusing but the point is: you should double check each data published to client via publications or manipulated / created via methods on server. If your backend is robust and secured as much as possible (100% will never be possible), then you don't need to care, if clients can see which routes exist and get access to them.
An example:
Bob found the route /admin and disabled the router's triggers to redirect him if he is not Admin.
Now Bob could see all data, that Admins can see. To prevent this, you may check in the publication if Bob has the role 'admin' on don't publish to him if he's not admin.
Ada also found this way to the Admin panel and wants to create a new user. Because your server method for creating new users is a ValidatedMedthod that checks if Ava has the 'admin' role it will fail to create a new user, because Ava is not an admin.
Conclusion:
Preventing access on the client side is just UI Magic bot not secure at all. Simple tweaks, however on the server side will help you to sleep better.
More help:
A good way to get started is to read the Meteor Security Guide:
https://guide.meteor.com/security.html
There is also at the end a security checklist, which I just cite for completeness and SEO reasons here:
Make sure your app doesn’t have the insecure or autopublish packages.
Validate all Method and publication arguments, and include the audit-argument-checks to check this automatically.
Deny writes to the profile field on user documents.
Use Methods instead of client-side insert/update/remove and allow/deny.
Use specific selectors and filter fields in publications.
Don’t use raw HTML inclusion in Blaze unless you really know what you are doing.
Make sure secret API keys and passwords aren’t in your source code.
Secure the data, not the UI - redirecting away from a client-side route does nothing for security, it’s just a nice UX feature.
Don’t ever trust user IDs passed from the client. Use this.userId inside Methods and publications.
Set up browser policy, but know that not all browsers support it so it just provides an extra layer of security to users with modern
browsers.
Useful packages mentioned in the answer:
mdg:validated-method
alanning:roles
audit-argument-checks
I have one app on abc.website.com that authenticates users using devise with Ruby on Rails. I have to setup a react app on def.website.com that only works if the user is logged in on abc.website.com (abc uses devise)
We have to add a link in the navbar of abc.website.com that redirects to def.website.com.
Any suggestions on what the best way to do this is?
I'm a total ruby noob but good with javascript.
thanks a lot
appreciate the help
Here is one of a solution : Token Based Authentication.
JWT also is a fine idea.
The rails app can also store a cookie (for example use the rails session cookie) in the clients cookie store. This gets sent to the rails app on every request by the react app and you can verify the identity of the user by reading this cookie.
There are very few adjustments needed for this approach (and they would also be required for JWT - which I don't want to badmouth, it is also a viable and good approach).
An upshot of this is that almost no data needs to be stored at the client. Only an ID that makes it easy for the rails backend to identify the user and achieve all needed informations.
On the other side, if you want to store all the data at client side this is also possible.
I would say there is no clear advantage of rails session over JWT or vice versa. But generally this is how it should be implemented: By giving the client the necessary information which then gets passed over to the backend again.
I've read about Firebase and it looks awesome for what I want to do.
I've read about authentication and how based on rules certain logged-in users are authorized to do different stuff. Al good.
However, I'm unsure about another type of security: how do I make sure that only my own site (using client-side javascript) can talk to my firebase-backend? I'm asking because afaik there's no way to prevent anyone from looking up my firebase endpoint from the client-side code (url pointing to my specific firebase backend) and start using that for god knows what.
This is especially worrisome in situations in which I want to open up writes to the anonymous user role. (e.g: some analytics perhaps)
Any help in clearing my mind on this much appreciated.
Update (May 2021): Thanks to the new feature called Firebase App Check, it is now actually possible to limit calls to your backend service to only those requests 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 Kato describes below, so that you have another shield against abusive users that do use your app.
In my opinion, this isn't so much a question about Firebase's security as a general discussion of the internet architecture as it stands today. Since the web is an open platform, you can't prevent anyone from visiting a URL (including to your Firebase) any more than you can prevent someone from driving past your house in the real world. If you could, a visitor could still lie about the site of origin and there is no way to stop this either.
Secure your data with authentication. Use the Authorized Domains in Forge to prevent CSRF. Put security rules in place to prevent users from doing things they should not. Most data writes you would use a server to prevent can be accomplished with security rules alone.
This is actually one of the finer qualities of Firebase and API services in general. The client is completely isolated and thus easily replaced or extended. As long as you can prove you're allowed in, and follow the rules, where you call in from is unimportant.
Regarding anonymous access, if you could make them visit only from your site, that still won't stop malicious writes (I can open my JavaScript debugger and write as many times as I want while sitting on your site). Instead, place tight security rules on the format, content, and length of data writable by anonymous users, or save yourself some time and find an existing service to handle your analytics for you, like the ubiquitous Google Analytics.
You can, of course, use a server as an intermediary as you would with any data store. This is useful for some advanced kinds of logic that can't be enforced by security rules or trusted to an authenticated user (like advanced game mechanics). But even if you hide Firebase (or any database or service) behind a server to prevent access, the server will still have an API and still face all the same challenges of identifying clients' origins, as long as it's on the web.
Another alternative to anonymous access is to use custom login, which would allow a server to create its own Firebase access tokens (a user wouldn't necessarily have to authenticate for this; the signing of the tokens is completely up to you). This is advantageous because, if the anonymous user misbehaves, the access token can then be revoked (by storing a value in Firebase which is used by the security rules to enforce access).
UPDATE
Firebase now has anonymous authentication built into simple login, no need to use custom login for common use cases here.
I have an application which uses Backbone JS to render its views (among other things like models etc.).
Parts of my application should only be displayed to admin users, but the only way for Backbone templates to know this is for some JS variable/localStorage/Cookie to be set to indicate such.
The first worry here would, of course, be security. I am only really storing things like User.id, User.isAdmin etc. in here. Furthermore, since the only things limited to administrators would involve server-side involvement, changing User.isAdmin = true on the client side will just give them the interface options, but none of the power.
I know that using Cookies for this sort of thing is probably frowned upon, and with good reason, since Cookies are sent to and fro across all HTTP requests creating unnecessary overheads.
Therefore my question is, how best should I tell my client-side application about User authentication data. I am using jStorage (http://www.jstorage.info) for localStorage access, but it does not use SessionStorage. Would the latter be more appropriate than the former?
Speaking generally, you need roles and possibly privileges on the server. This way, even if a nefarious user tweaks the client, user cannot actually access or modify restricted data, because if they try, they still havent modified the role on the server. If you audit log access, you will know fairly quickly if a user tries something for which they don't have privileges.
The worst that can happen under a scheme like this is the user could see what the admin screens look like. This still might be a breach, since they might see buttons that could inform them how the system works.
If you need to protect that, you could delay the loading of the templates for the "Admin Section" of your app until after a users role has been verified. So all restricted sections are almost loaded as a separate app (or an actual separate app). That way, even if the user tries to fool the client to load the admin section, when the request goes to the server you don't return the module, since the server will not show the user as having admin privileges.