Is API key exposed through get request? - reactjs

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:

Related

node paymentMethods.list equivalent in #stripe/react-stripe-js

I am trying to retrieve all payment methods listed for a customer in stripe using react. I have access to publishable key and client secret.
But, I am unable to find a method to retrieve the payment methods (similar to the one in node - stripe.paymentMethods.list or PaymentMethodsRetrievalListener in Android).
Any help, much appreciated.
Regards,
There isn't such a method unfortunately!
The backend API for listing PaymentMethods (https://stripe.com/docs/api/payment_methods/customer_list) generally requires a secret key(which is what stripe-node uses), not something you can use in a React frontend.
The mobile SDKs like Android use the ephemeral key they get from your backend (https://stripe.com/docs/payments/accept-a-payment?platform=android&ui=payment-sheet#add-server-endpoint) to call the same API — it's technically possible to do this yourself on the web too but it's not documented in any way so it's not really a good option.
Overall you would normally just have your frontend call your backend, your backend(written in e.g. Node and using your secret API key) can call https://stripe.com/docs/api/payment_methods/customer_list and return the information the frontend is looking for.

Securely storing third party API secrets in React & Shopify

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.

Anyone can fetch my blog posts using my GET end-points and use them on his own site? Is there a way to protect this?

I have a blogging app built on top of the MERN Stack. I am fetching my blog posts on the react front-end, however, I feel anyone can use my blog posts on his own site by hitting the same end-point. I want to protect this behaviour. Is there a way?
If, for some reason, it isn't enabled already, make sure your endpoints have standard Access-Control-Allow-Origin restrictions - that is, that they only permit direct connections from your domain, not from other sites. This will make it slightly more difficult for other sites to scrape yours, because they won't be able to make requests directly from the frontend.
You could also change your application structure so that the blog data gets sent with the initial HTML response. For a small example, you could have
<script type="application/json" class="blog-data">
[{"title":"some post title", "content":"some content"}]
</script>
const blogData = JSON.parse(document.querySelector('.blog-data').textContent);
This will also make it a bit harder for a scraper to work - they won't have an endpoint ready to serve the plain blog data, they'll have to parse through your HTML response first.
You could also frequently change up the DOM structure of the data in the HTML response to make it harder.
But web scraping is fundamentally nearly impossible to stop, for someone who's determined enough.
Basically, you can use CORS on your backend to protected fetching your endpoints from any browsers origins except allowed ones.
Anyway it will not help you to protect from calling API from such things like mobile apps, Postman etc.
If you worry about loading to the server you can add something like rate limiting.
But keep in mind if your API is public it will be public for all, you can't restrict to use it from your site only.
Here are a few ideas:
Maybe add some authentication to protect your endpoints.
If you are using CORS, only accept requests from a certain URL.
In your package.json, add a proxy.

React, API keys and intermediary services

I am kind of new to React, so this might be just lack of experience, but I don't seem to find any answer to my question:
I have a react app, where I need to subscribe to a push notification channel. Messages are delivered through PubNub, and in order to connect I need to supply a subscribe and a publish key to the message server. Now, I know it is not a good practice to store secrets in a react app, and they should be handled through backend services, but do I really need to create a service just to subscribe to the channel and forward the messages to my frontend app? Is this not an overkill?
The messages I am receiving are just time ticks (I need a trusted source of time), but I still don't want my API keys to leak out...
Is there any reasonably ok way for me to avoid standing up an intermediate service?
It is perfectly normal to have your PubNub publish and subscribe keys in client side code. If it is necessary to restrict who has the power to publish and subscribe (read/write) using those keys, the developer can enable PubNub Access Manager (PAM) in the admin panel. There are PAM guides to get you started on controlling access.
Another point to consider is that your JavaScript PubNub connection can also be used as a trusted source of time. The JS SDK time call will get a 17 place precision unix timestamp from a PubNub node:
const pubnub = new PubNub({
publishKey: 'your_free_pubnub_publish_key',
subscribeKey: 'your_free_pubnub_subscribe_key'
});
let pojoDateObject;
pubnub.time().then((timetokenObject) => {
pojoDateObject = new Date(+String(timetokenObject.timetoken).substring(0,13));
});

Serve REST-API data in web-page without exposing API-endpoint

I am beginner in MEAN stack.
When invoking unauthenticated REST API (no user log-in required), API end-points are exposed in the JS files. Looked through forums that, there is no way to prevent abusers using the API end-point directly or even creating their own web/app using those end-points. So my question is, are there any way to avoid exposing the end-points in the JS files?
On a similar note, are there any ways, not to use REST calls on front-end and still be able to serve those dynamic content/API output data in a MEAN stack? I use EJS.
There's no truly secure way to do this. You can render the page on the server instead, so the client only sees HTML (and some limited JS).
First, if you don't enable CORS, your AJAX calls are protected by the browser, i.e. only pages served from domain A can make AJAX calls to domain A.
Public API providers like Google Maps protect themselves by making you use an API key that they link to a Google account.
That key is still visible in the JS, but - if abused - can be easily disabled.
There's also pseudo-security through obfuscation, i.e. make it harder for an attacker to extract a common secret you are using the encrypt the API interaction.
Tools like http://javascript2img.com/ are far from perfect, but makes attackers spend a lot of time trying to figure out what your code does.
Often that is enough.
There are also various ways to download JS code on demand which can then check if the DOM it runs in is yours.

Resources