Protect Your Application by Hiding API Keys and Tokens in React

Written by ljaviertovar | Published 2023/01/19
Tech Story Tags: reactjs | typescript | typescript-tutorial | react | web-development | nodejs | frontend-development | javascript

TLDRSome applications have API keys, tokens, or other sensitive credentials visible in their source code. If this data is exposed in your application’s source code, anyone who can see your code can access it and use it to perform unauthorized actions on your behalf. By exposing your credentials, you are allowing third parties to use them, which can affect the performance or quality of your services, or even perform attacks against them.via the TL;DR App

Using environment variables in Reactjs with Vite and nodejs

Have you ever noticed that some applications have API keys, tokens, or other sensitive credentials visible in their source code? or maybe your own applications?

Well, this can be dangerous and put the security of your services at risk.

API keys, tokens, and other sensitive credentials are used to authenticate and authorize access to services and APIs. If this data is exposed in your application’s source code, anyone who can see your code can access it and use it to perform unauthorized actions on your behalf.

Moreover, by exposing your credentials, you are allowing third parties to use them, which can affect the performance or quality of your services, or even perform attacks against them.

This tutorial will show us how to hide your API keys, tokens, and other sensitive credentials.

How to hide sensitive data?

Some of the ways to do this are:

  • Use environment variables

  • Use a proxy server or a backend platform as a service (BaaS)

  • Use a secure hosting platform

In this way, you can protect the security of your services and prevent them from being compromised. Now, we will see how to hide sensitive credentials by building a small backend for our React application.

Using Environment Variables in React with Vite

Note: This is just the first step, your application will remain vulnerable until now.

In this tutorial, we will add an OpenWeather APY KEY as an environment variable.

Specifically, we will use the weather API, which is a service that provides weather data, and after a certain number of requests, it starts charging for the requests, so we don’t want this API KEY available to the public.

To configure the environment variables in React, with Vite, we can follow these steps:

1 — Create your React application with Vite. I will use TypeScript but you can use JavaScript if you wish.

2 — Create a .env file at the root of your project. This file should contain the environment variables you want to use in your application.

API_KEY=784dc6d4eXXXXXXXXb14460d3a356565
PORT=3001

Once we create our .env file we must not forget to attach it to the .gitignore file of git, otherwise, our API KEY will be exposed in the repository.

3— In Vite, to access the environment variables we need to access them via import.meta.env. This returns an object containing the available environment variables known to Vite.

The variables created must have the prefix VITE_ or could be another prefix overwriting the default configuration option envPrefix.

const API_KEY = import.meta.env.VITE_API_KEY

4 — Now we will create a simple application that will fetch the Weather API to retrieve the temperature of a city.

import { useEffect, useState } from "react"
import { ResponseWeather } from "../types"

const API_KEY = import.meta.env.VITE_API_KEY

import "./App.css"

function App() {
 const [weather, setWeather] = useState<ResponseWeather | null>(null)

 useEffect(() => {
   const options = {
     method: "GET",
     url: `https://api.openweathermap.org/data/2.5/weather?lat=43.7001&lon=-79.4163&appid=${API_KEY}`)
     headers: {
      "Content-Type": "application/json",
     },
  }

  axios.request(options).then(response => {
   setWeather(response.data)
  })

 }, [])

 if (!weather) return null

 return (
  <div className='App'>
   <h1>{weather?.name}</h1>
   <h2>{Math.floor(weather?.main.temp - 273.15)} °C</h2>
   <i>
     <img
       src={`http://openweathermap.org/img/wn/${weather?.weather[0].icon}@2x.png`}
       alt={weather?.weather[0].description}
      />
   </i>
   <p>{weather?.weather[0].main}</p>
  </div>
 )
}

export default App   

Now that we have hidden our API KEY with an environment variable and made sure that the .env file is not uploaded to the repository, we have done half the work.

UPDATE: although it is not exactly half of the work to keep our application secure it would be all we can do on the client side. Make sure that we don’t save in the repository that is public the keys or sensitive variables for the project. Since it would be another vulnerable access route, it’s possible that someone may not be able to access the application on the web, but may be able to find the repository of the project in some way.

And why do we say this? Because our API KEY is still exposed in the final build. If we inspect our app in the browser we will find the value of the API KEY, so the only way to have this value completely hidden is to have it on the server side creating a backend service.

This backend will be in charge of making requests to the Openweather service and our client, that is, our React application will query this backend.

pnpm express cors dotenv axios

In this case that we use Typescript, we will need in addition

pnpm ts-node @types/express @types/cors @types/dotenv @types/node

  • express to create a server

  • cors to enable resource sharing between different domains.

  • dotenv to load environment variables.

  • axios to fetch data.

2 — We create a file called api.ts at the root of the project. We also take the opportunity to add a new command to  package.json to boost our server.

"scripts": {
  "server": "npx ts-node api.ts",
  ...
}

3 — We create the server and make available the endpoint that we will use from React.

import express from "express"
import { Request, Response } from "express"
import * as dotenv from "dotenv"
import cors from "cors"
import axios from "axios"

dotenv.config()

const app = express()

app.use(cors())

app.get("/api/weather", (req: Request, res: Response) => {
 const options = {
  method: "GET",
  url: `https://api.openweathermap.org/data/2.5/weather?lat=43.7001&lon=-79.4163&appid=${process.env.VITE_API_KEY}`,
  headers: {
   "Content-Type": "application/json",
  },
 }

 axios
  .request(options)
  .then(response => {
   res.json(response.data)
  })
  .catch(err => {
   console.log(err)
  })
})

app.listen(process.env.VITE_PORT, () => console.log(`Server on port ${process.env.VITE_PORT || 3001}`))

Now that we have the backend service, our API KEY is completely hidden from the client. We could add more layers of security as we want for example, in the cors add only the domain where our app is hosted and only that domain can make requests.

4 — We start our server with the following command

pnpm run server

Finally, we just need to replace the URL of the request we make in React with the one we have in the backend. We no longer need to retrieve the API KEY in React.

// Before
url: `https://api.openweathermap.org/data/2.5/weather?lat=43.7001&lon=-79.4163&appid=${API_KEY}`)

// After
 url: "http://localhost:3001/api/weather"

And that’s it! our application continues to work as usual only that we no longer have our API KEY exposed on the client side.

Repo here.

Conclusion

In conclusion, hiding sensitive data is something relatively easy, just follow the steps mentioned above and with that, we can avoid major risks and complications.

I hope this will help you to keep your projects safe.


Also published here.


Written by ljaviertovar | ☕ FrontEnd engineer 👨‍💻 Indie maker ✍️ Tech writer
Published by HackerNoon on 2023/01/19