Securing JS client-side SDKs - javascript

I'm working on a React-Redux web-app which integrates with AWS Cognito for user authentication/data storage and with the Shopify API so users can buy items through our site.
With both SDKs (Cognito, Shopify), I've run into an issue: Their core functionality attaches data behind the scenes to localStorage, requiring both SDKs to be run client-side.
But running this code entirely client-side means that the API tokens which both APIs require are completely insecure, such that someone could just grab them from my bundle and then authenticate/fill a cart/see inventory/whatever from anywhere (right?).
I wrote issues on both repos to point this out. Here's the more recent one, on Shopify. I've looked at similar questions on SO, but nothing I found addresses these custom SDKs/ingrained localStorage usage directly, and I'm starting to wonder if I'm missing/misunderstanding something about client-side security, so I figured I should just ask people who know more about this.
What I'm interested in is whether, abstractly, there's a good way to secure a client-side SDK like this. Some thoughts:
Originally, I tried to proxy all requests through the server, but then the localStorage functionality didn't work, and I had to fake it out post-request and add a whole bunch of code that the SDK is designed to take care of. This proved prohibitively difficult/messy, especially with Cognito.
I'm also considering creating a server-side endpoint that simply returns the credentials and blocks requests from outside the domain. In that case, the creds wouldn't be in the bundle, but wouldn't they be eventually scannable by someone on the site once that request for credentials has been made?
Is the idea that these secret keys don't actually need to be secure, because adding to a Shopify cart or registering a user with an application don't need to be secure actions? I'm just worried that I obviously don't know the full scope of actions that a user could take with these credentials, and it feels like an obvious best practice to keep them secret.
Thanks!

Can't you just put the keys and such in a .env file? This way nobody can see what keys you've got stored in there. You can then access your keys through process.env.YOUR_VAR
For Cognito you could store stuff like user pool id, app client id, identity pool id in a .env file.
NPM package for dotenv can be found here: NPM dotenv
Furthermore, what supersecret stuff are you currently storing that you're worried about? By "API tokens", do you mean the OpenId token which you get after authenticating to Cognito?

I can respond to the Cognito portion for this. Your AWS Secret Key and Access Key are not stored in the client. For your React.js app, you only need the Cognito User Pool Id and the App Client Id in your app. Those are the only keys that are exposed to the user.
I cover this in detail in a comprehensive tutorial here - http://serverless-stack.com/chapters/login-with-aws-cognito.html

Related

How can Firebase Authentication securely authenticate a user with only client-side code?

There is plenty of tutorials and articles on this precise question but each one contradict the previous one,
I'm trying to make a signup and login reactJs pages with Firebase js sdk on the frontend, that's what I found most of youtubers devs do,
And then I found that is not secure (doing the authentication on client side).
You should use the Firebase Admin SDK (firebase.google.com/docs/admin/setup) on Firebase Cloud Functions or a self-hosted server in that case. Everything else would just be a dirty hack – PRSHL source
It's not recommended to create admin accounts on the front end as anyone could look into the code and action it themselves. source
I really want to understand if it is not secure to use it on the client side, Why does firebase provided it in the first place ?? or is there another way to properly write the auth using firebase js sdk on the frontend ? of course without using admin sdk
Or should I use firebase js sdk on the backend with express ?
I only want clear and detailed answers please !!
My best guess is that you're confused between authenticating a user client-side and the fact that Firebase provides a client-side SDK for authenticating users.
Though all you have to do to use Firebase Authentication in your app is implement its client-side SDK, there are many more parts involved in the process - and quite a few of them run on secured servers.
It's just that Firebase (and the authentication providers it supports) have implemented the server-side of the authentication process for you already and made the variables parts of the process part of the configuration that you provide either in the Firebase console, the provider's web interface, and/or in the configuration that you specify when you initialize the Firebase SDK in your client-side application code.
From the comments you now added, the second is correct and explains exactly what the risk is:
It's not recommended to create admin accounts on the front end as anyone could look into the code and action it themselves.
So while you can safely create a user account on the client (a process known as authentication), marking them as an admin (a process known as authorization) has to happen in a trusted environment as otherwise any user could make themselves an admin.

I want to limit the total number of articles read on my webpage by a user, without signup, like how medium does

I don't have any idea how to implement this. After a bit of search I found out that medium keeps track of the browser and not the user, what is mean is you can access three free articles from each new browser on the same machine (if I am wrong do point it out). I am using React and Firebase for my website.
Edit: I was thinking along the lines of getting some kind of id which is unique to a browser. As cookies and local storage can always be bypassed.
I don't know if it's a clean way to do it but you can associate an IP to an unique counter. Or with a cookie but he can bypass that by cleaning the cookies
The answer would tightly depend on your application setup and especially on the service backing your front store.
If you are using a self-backed backend, for example a nodejs - express based server, within your route middleware you can access the remote address from the req.connection.remoteAddress request property along with the user-agent req.header('User-Agent') and forward these to your datastore being Firebase in this case.
If you are deploying your application to Google Cloud Function, you can then access the remote peer address using the fastly-client-ip request header and still forward this to your storage system.
Use javascript and implement a system that uses a cookie or local-storage to verify how many articles are read on your website.
On most of these websites however you are still able to bypass this limit by clearing the cache or using a incognito window.
To also limit these scenarios you can use a cookie in combination with an IP address, which has its own drawbacks, especially in corporate environments, and mobile connections where IP addresses are heavily shared or changed. Depending on your situation this may matter or not.

Firebase DB- JS library - protect /secure data read [duplicate]

I think from searching the web this is not technically possible but I want to ask again in case I'm missing something.
I have an app that uses Firebase. Reading and writing is locked down through security rules for authorised users only but there's certain information I want unauthorised users to be able to access (so I don't have to put a login wall in front of them, influencing churn).
What I want to know is, is there any way of locking down this read access that only my app can call the DB? I know I can lock down domains to prevent someone writing localhost scrapers but what's to stop someone cloning and re-skinning an app and pointing it to the same back end? Is it possible to achieve this using your certificates fingerprint?
There is no way to limit access to your database to just your app. That just doesn't match with the cloud-based nature of the Firebase APIs. Anyone that knows the URL of your database can in in principle access it, and security rule are the way to ensure all access is authorized.
Note that security rules are not an all-or-nothing approach: you can require sign-in for some parts of your database, while leaving other parts publicly readable. But you can't make the publicly readable parts only be readable by your own app.
Some previous questions on the same topic:
how to make sure only my own website (clientside code) can talk to Firebase backend? (pretty much my go-to answer for this)
How to allow only my app to access firebase without a login?
Restrict Firebase database access to one Android app
How to allow only my app to access firebase without a login?
Update: since May 2021 you can actually restrict access to just users of your App by implementing Firebase App Check.
I have found a solution that maybe helps you or anyone that have a similar question. I answered it in this question:
Restricting Cloud Firestore to a specific domain

Condition based access to amazon lamda results?

Wondering if it's possible to have a webapp upload a file (userid.input.json) to Amazon S3, which triggers a lambda function that reads the file, does some processing, and saves the result as another (userid.output.json).
However userid.output.json should not be immediately accessible to the web application. The webapplication has to complete a Stripe payment and once the payment completes, the web application can access the (userid.output.json) file on amazon s3.
Before I ask how, I figured I'd first ask if this this scenario can be facilitated / architected on AWS?
Approach
Note that this is an update to the question based on more research. It looks like Amazon Cognito will be the perfect tool for signing in users and tying their user credentials to an IAM role that can read and write to S3 buckets.
So once the user is signed in through Amazon Cognito and has the proper credentials then their files can be uploaded to an S3 bucket and processed by a lambda. The result is then written to the same bucket.
Now earlier I suggested writing to a sealed bucket and having a Stripe webhook trigger moving the result from the sealed bucket to an accessible bucket. But it seems this is necessary, per the indication in the answer provided by #Snickers3192.
Once the stripe payment completes the webapp can set a boolean that is used to control access to the output and that completes the cycle?
Part of the rational for having a hidden bucket was that someone might pull the credentials out of the browser and execute them in a different script. I assume this is impossible (Famous last words :) ), but just in case I wrote a follow up question here.
In other words the credentials that are pulled into the client post signin with Amazon Cognito cannot be used to executed scripts outside of the application context?
Approach Part 2
Per my follow up questions it does not appear that relying on state within the webapp for making security decisions is good enough, as someone can probably figure out a way to get the token authentication token and manipulate the applications API directly using a client other than the core app.
So now I'm thinking about it like this:
1) Write the result to the sealed bucket (Processing Lambda)
2) Have the Stripe webhook update the users a transaction record in the users profile indicating payment paid = true (Stripe Lambda)
3) Create another lambda that has access rights to the sealed bucket but will return results only if paid=true. (Access Result Lambda)
So since Stripe is tied to an IAM user that is allowed to update the Application user profile and set paid=true and the sealed bucket can only be accessed by lambda that first checks if paid=true before returning the result, I believe that should guarantee security.
If anyone has a simpler approach please let me know.
This really is more a question of where you want to put the security, which in AWS there are many options, in your application logic which could mean:
Lambda/Webapp
S3 policies
IAM roles/groups
These decisions are usually dictated by where your identity store is kept, and also if you want to keep the notion of AWS users VS. users of your app. My preference is to keep these two pools separate, in that security logic like this is kept in the webapp/lambda and AWS security only deals with what rights developers have to the environment as well as what rights applications themselves have.
This means the webapp can always access the input and output buckets, but it keeps a record in a database somewhere (or makes use of your payment system API) who has paid and who hasn't paid and uses that information to deny or grant access to users. IMO this is a more modular design, and it enables you to lock down your AWS account better and is more clear to developers where security is located. In addition if you do go with IAM/S3 it will be more difficult to run and debug locally.
EDIT: After all your comments and additional security concerns you may also want to consider emailing a short lived URL link to the processed file, so that a user needs both email access as well as knowing their credentials to the application. This will mean even if your access token is stolen at the browser level, without the email access a hacker still can't get the processed file. If you want to be EXTREME SECURITY CORE, have the link that not only is authentication required, but also MFA so that they need to enter in a code which is constantly refreshing as you should have setup for your AWS account when you login.
I'm by no means a security expert but just follow best practices and do your due diligence and you will meet security expectations.

Web site using backbone for frontend and nodejs for backend

I'm developing a new web site that will be a single paged app with some dialog/modal windows. I want to use backbone for frontend. This will call backend using ajax/websockets
and render the resulting json using templates.
As a backend I'll use nodejs express app, that will return the json needed for client, it'll be some kind of api. This will not use server side views.
Client will use facebook, twitter, etc. for authentication and maybe custom registration form.
Client static resources, such as css, js, and html files will be handled by nginx (CDN later).
Questions that I have now:
How can I determine that a given user has the right to do some action in api(i.e. delete a building, create new building)? This is authorization question, I thought of giving user a role when they login and based on it determine their rights. Will this work?
Similar to the above question, will this role based security be enough to secure the api? Or I need to add something like tokens or request signing?
Is this architecture acceptable or I'm over engineering and complicating it?
Passport is an option for the authentication piece of the puzzle. I'm the developer, so feel free to ask me any questions if you use it.
I thought of giving user a role when they login and based on it determine their rights. Will this work?
Yes this will work. You can check for a certain role on the user after it's been fetched from the server. You can then display different UI elements depending on this role.
Will this role based security be enough to secure the api? Or I need to add something like tokens or request signing?
It wont be enough. Anyone could hop into the console and set something like user.admin = true. In your API you'll need to validate a user token from the request, making sure that the related user has the appropriate permissions.
Is this architecture acceptable or I'm over engineering and complicating it?
At the least you should have an API validation layer. That would make a decent enough start, and wouldn't be over-engineering.
For the authentication part of your question i would use everyauth which is an authentication middleware for connect/express. It supports almost every oauth-social-network-thingie.
For role management you could give node-roles a try. I didn't use it myself but it should help you out, because it checks the role on the server side. Of course that is only useful if your API is implemented in node.js. If that's not the case, you have to "proxy" the API calls over your node.js app.
I hope I could help you! :)

Categories

Resources