Securely storing third party API secrets in React & Shopify - reactjs

Firstly, I am fairly new to both React and Shopify, so please bear that in mind with your answers.
I have created a basic Shopify app using their CLI tools which provide a React app. I now need to connect this app to a third party that manages custom shipping options. I therefore need to authenticate with this third party which then returns a token which I can use in my API calls. I've read many answers here about storing such tokens, some recommend localstorage/cookies, others state never do that but don't provide a clear answer to what one SHOULD do instead.
Currently I have something like the following:
let data = {
grant_type: 'client_credentials',
client_id: process.env.REACT_APP_THIRDPARTY_API_KEY,
client_secret: process.env.REACT_APP_THIRDPARTY_API_SECRET
}
axios.post('https://oauth.somethirdparty.se/v1/token', data).then(res => {
if (typeof window !== 'undefined') { // Check for browser
localStorage.setItem('t', res.data.token);
}
});
However I receive "undefined" errors for those env vars, and therefore the axios.post fails (works fine if I put in the key/secret directly here instead of the .env). Aside from this being unsecure according to the many posts here, I'm wondering if I can perhaps do something similar to what Shopify is doing, only my lack of knowledge prevents me from understanding exactly it is that they are doing!
The generated Shopify app uses the .env file in it's server.js file, like so:
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SCOPES.split(","),
HOST_NAME: process.env.HOST.replace(/https:\/\//, ""),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
// This should be replaced with your preferred storage strategy
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
How can one safely store API credentials in my case? And please provide an actual example.
EDIT
I've found that if I modify the Shopify server.js file to console.log(process.env), I see all of the used env vars in the Terminal, and I guess the reason these are "undefined" when I try to log them in my app component is intentional so they are not exposed, which is great. Unfortunately it still doesn't help me when I need to connect to a third party service and get a token etc - how do I do that in this case?

This is a very easy question to answer. It is true, you always want to store your secrets in something like a dotenv file. Modern advanced frameworks like Rails even let you encrypt those, although eventually, you do need to ensure a secret key is present on your server for that.
So your public hosting service allows you to set environment variables. That is where you ensure they exist. You do not check those values into your public/private GitHub copy of your code.
So now, when your code executes, it has access to your secrets. It seems when you run your code, and you get undefined values, it is due to this, you have failed to set your environment properly. Read the documentation at your hosting service to figure that out.
Note that Shopify is not unique, 99% of all services operate this way. So you should have no trouble finding an answer to your problem.

Related

Is API key exposed through get request?

I am building a Node/React app in which I have placed my API keys in a .env file which is in my .gitignore. The frontend makes a get request to the API endpoint using Axios and the UseEffect hook with the API key provided via process.env. I understand why it is good practice to obscure the API key and not commit that information to git however my question is whether something still needs to be done (or can be done) about the API key getting exposed through inspection of the requests in chrome developer tools?
//on component mount fetch the images
useEffect(async ()=>{
const results = await axios(
`https://pixabay.com/api/?key=${process.env.PIXA_API_KEY}`
);
},[])
For instance below if a user were to use chrome tools in the browser on my project they can still see my API key as part of the request. In my case it's not much of a concern as this particular API is free and the project is for personal use only, but I wondered how this problem is approached in a commercial project where a payed for API might be in use? What's to stop me using chrome dev tools on another persons app and stealing their API key to make my own requests?
That is a very good observation. The truth is that you cannot have any secrets in your client code. No amount of obscuring, obfuscation or even encryption will prevent attackers from stealing your secrets. The client code is out there for anyone to read and needs to be approached as such.
If private APIs with keys you do not want to expose are involved, you need to call them from a server. So the flow would look something like this:

Using Firebase Remote Config instead of .env file

I am building a react application with firebase integration and the environment variables we are using can be inspected out by taking the page source of the page in a deployed website.
I am interested in knowing some ways to make it safer. the only way I can think is of take values from an API so that its not shown with the code at any point.
To connect to firebase I can use the reserved url method to automatically connect.Firebase remote config allows you to store key value pairs. I was thinking of moving all my env variables out to remove config setup and use it from there. So I can remove my .env file altogether and avoid exposing any hardcoded values.
Have anyone tried this already? what could be the recommended way to make .env values safer?
You should never load any values that you don't want users to be able to access into the browser, period. The browser is an open book and while you may be able to obscure values by changing where and how they are loaded, you cannot prevent a motivated attacker from reading absolutely anything and everything you do on the client.
This is why Firebase is designed to have API keys and configuration that are safe to be publicly readable -- when you write security rules you are essentially drawing boundaries around what clients can do.
Firebase Remote Config can and should be used for values that are safe for clients to have -- things like feature flags or environment-specific URLs for APIs. It should not be used for sensitive things like private API keys and secrets.

How to use external api access tokens without exposing them to the user?

Sorry if this might be a bit of a trivial question, but I wanna be sure and couldn't exactly find a definitive answer online.
I am writing a small app that uses Mapbox, and I am using react-map-gl for it. They require the access token on the client side, so they suggest using an environment variable. My question is would it be okay to simply create a .env file in the front-end folder and put the variable there?
Thanks!
You can't get away from revealing API keys on the front end. If someone wants to dig around in your source code, they will find them.
However, you should always configure any API key that is visible on the Internet to be restricted to specific referrers, i.e. the domain of your website.
Usually this is done during creation of an API key through your provider's dashboard.
For Mapbox, you can read the documentation on restricting API tokens here. It states:
You can make your access tokens for web maps more secure by adding URL restrictions. When you add a URL restriction to a token, that token will only work for requests that originate from the URLs you specify. Tokens without restrictions will work for requests originating from any URL.
(emphasis my own)
They require the access token on the client side, so they suggest using an environment variable. My question is would it be okay to simply create a .env file in the front-end folder and put the variable there?
There are two reasons one uses environment variables in front-end development:
As a convenience, to keep environment-specific configuration removed from source code.
To keep sensitive information out of source code. You shouldn't commit API tokens or other similarly sensitive details to your version control.
Using environment variables in front-end code will not to keep their values secret from the end user. Whatever the value of an environment variable is at build time will be visible in the compiled output.

What's the best way to prevent React app being scraped?

I'm still very new to React so forgive me if the question is too naive. To my understand, React usually requires an API to do XHR requests. So someone with very basic tech background can easily figure out what the api looks like by looking at the network tab in web browser's debug console.
For example, people might find a page that calls to api https://www.example.com/product/1, then they can just do brute force scraping on product id 1 - 10000 to get data for all products.
https://www.example.com/api/v1/product/1
https://www.example.com/api/v1/product/2
https://www.example.com/api/v1/product/3
https://www.example.com/api/v1/product/4
https://www.example.com/api/v1/product/5
https://www.example.com/api/v1/product/6
...
Even with user auth, one can just use same cookie or token when they login to make the call and get the data.
So what is the best way to prevent scraping on React app? or maybe the api shouldn't be designed as such, hence I'm just asking the wrong question?
Here are some suggestions to address the issue you're facing:
This is a common problem. You need to solve it by using id's that are GUID's and not sequentially generated integers.
Restricting to the same-origin won't work because someone can make a request through Postman or Insomnia or Curl.
You can also introduce rate-limiting
In addition, you can invalidate your token after a certain number of requests or require it to be renewed after every 10 requests
I think no matter what you do to the JavaScript code, reading your API endpoint is the easiest thing in the world(Wireshark is an easy, bad example), once it is called upon from the browser. Expect it to be public, with that said, protecting it it is easier than you might anticipate.
Access-Control-Allow-Origin is your friend
Only allow requests to come from given urls. This may or may not allow GET requests but it will always allow direct access on GET routes. Keep that in mind.
PHP Example
$origin = $_SERVER['HTTP_ORIGIN'];
$allowed_domains = [
'http://mysite1.com',
'https://www.mysite2.com',
'http://www.mysite2.com',
];
if (in_array($origin, $allowed_domains)) {
header('Access-Control-Allow-Origin: ' . $origin);
}
Use some form of token that can be validated
This is another conventional approach, and you can find more about this here: https://www.owasp.org/index.php/REST_Security_Cheat_Sheet
Cheers!

React JS - Best practice to hide http call credentials

What's the best practices to hide or prevent the user see the credentials (implemented in WebService calls). The development is ReactJS and use Heroku to deploy the WebApp.
I have this code:
I want to prevent the user can see the credentials and some security details.
I started using the node module dotenv recently and really like how easy it is to use. All you need to do is install it and create a .env file with your environment variables like this:
.env
SECRET_KEY=123456
ANOTHER_KEY=78901
Then, require it as early as possible in your application:
require('dotenv').config().
I do this inside my server.js file (or whatever you name it).
That's it! Anything stored in the file can now be accessed by doing process.env.{name}
For example:
let secret = process.env.SECRET_KEY;
console.log(secret); // 123456
This is not really possible to do in a client side because all the HTTP calls can be easily visible in Network tab in Chrome Inspect Elements(or any web browser).
I would suggest you work on the security so you don't care if a user will see your HTTP endpoints or not.
You can also consider making your HTTP request on a server which will act as a bridge between your client and an API.

Resources