In frontend development, proper measures have to be taken to ensure that secrets and credentials are properly stored and managed as they have the potential to cause havoc with reckless practices. In this article, we will be looking at the best ways to manage secrets and keys, usually from APIs.
APIs could be private or public. A private API is developed and hosted by in-house developers within an organization, and they’re not shared or used by developers outside that organization. Private APIs are easily managed because the developers have full control of how applications are developed with it.
A good example of a private API would be an API developed by a backend developer that allows you, the frontend developer, access to your organization’s data. Since private APIs are restricted, there is no need to include a key or secret before using the API.
On the other hand, a public API is a service offered by a third party that is publicly available and provides access to a proprietary software application or web service. As the name implies, public APIs are available for all developers both within and outside the organization where it was developed.
They allow developers to leverage features already available to enhance their applications instead of building those features from scratch. A good example of a public API is the Google Maps API, which allows developers to use Google Maps within their applications.
Some service providers offer their public APIs for free for a lifetime, while others are paid or free for a specific number of requests. For effective authorization and authentication, API providers use keys and credential secrets that are unique to each user of the API. These keys and secrets must be safely managed and stored because they could otherwise pose serious challenges if they get into the wrong hands.
API keys and credential secrets that are not properly stored could cause financial, regulatory, or reputational damage.
I will be using React in the following code snippet, but the principles can be applied to vanilla JavaScript and other frameworks as well:
import React from "react"; const index = () => { const Api_key = "1234567" return( <> <p>Hello, Secrets </p> </> ) } export default index;
This is a bad practice because your credential secret can easily be extracted from the browser with the dev tools:
Control+Shift+I
static/js
main.chunk.js
You will find your credential secret, pretty easy for anyone to extract:
import emailjs from ‘emailjs-com’ function App(){ const handleSubmit = (e) => { e.preventDefault(); emailjs .sendForm(`gmail`, "876TY43sa23r56y789", e.target, process.env.REACT_APP_USER_ID) .then( (result) => { alert("Message Sent, We will get back to you shortly", result.text); }, (error) => { alert("An error occured, Please try again", error.text); } ); }; return( <> <form onSubmit={handleSubmit}> <input name="name"/> <input name="email"/> <button type="submit">Submit</button> </form> </> ) } export default App;
This is also a bad practice because anyone can access your repository online. Even if your repository is private, some hackers use GitHub crawlers to crawl repositories for credential secrets. A good solution to this is to store your credential secrets in a .env
file, which we will see in the next section.
If at any point you committed and pushed your API credentials to your Git repo, you should reset the key as quickly as possible. This can be done by accessing the dashboard from the API service provider or by removing all traces of it by using a Git rebase to remove the particular commit that added the keys.
Most API service providers allow you to restrict usage by setting a limit on the number of requests to be made per day and a particular URL from which the API can be accessed. From the image below, no domain was saved, so requests can be sent from any URL with the API credentials:
Some service providers allow you to set restrictions on the usage of the API key such that the API key can only be accessed from the URL you have specified. This means even if a hacker gets access to your key, it would be useless; it can only be used with the specified URL.
You can also set a daily limit on the usage of your API credentials. In the image below, requests to the API can only be made to the URL specified:
.env
) fileWith a .env
file, your secret is not directly in your code. This is particularly great with Git. You can upload your code to Git and add the .env
file to your .gitignore
file. This way, your .env
file will not be committed to GitHub. This can be done with the following steps:
.env
file in the root of your project:
- your_react_project_folder - public - src - node_modules - .env <-- your .env file - .gitignore - package-lock.json - package.json
.env
file, add REACT_APP_
as a prefix to your API key name and set the value (for React applications) and VUE_APP_
as a prefix to your API key name and set the value (for Vue applications). This allows the frameworks to identify the variables:
# .env REACT_APP_YOUR_API_KEY_NAME=your_api_key <-- for react apps VUE_APP_YOUR_API_KEY_NAME=your_api_key <-- for vue apps # Example: REACT_APP_TEMPLATE_ID=98765432123456789 REACT_APP_USER_ID=98765432123567 VUE_APP_USER_ID=98765432123456789
Add the .env file to your .gitignore file, this way your .env file is not committed to git and this hides your API key when you push your repo to GitHub:
#.gitignore file # dependencies /node_modules # env .env
Now you can use the API key in your code by appending it with process.env
:
//app.js //here I used api keys from emailjs already declared in the .env file. import emailjs from ‘emailjs-com’ function App(){ const handleSubmit = (e) => { e.preventDefault(); emailjs .sendForm(`gmail`, process.env.REACT_APP_TEMPLATE_ID, e.target, process.env.REACT_APP_USER_ID) .then( (result) => { alert("Message Sent, We will get back to you shortly", result.text); }, (error) => { alert("An error occured, Plese try again", error.text); } ); }; return( <> <form onSubmit={handleSubmit}> <input name="name"/> <input name="email"/> <button type="submit">Submit</button> </form> </> ) } export default App;
This is a good practice, but not a very secure one because your API key is still visible in the browser dev tools. Your API credentials will still become part of the build and would be visible to anyone who inspects your files, just like we did earlier.
You will find the API keys defined in the .env
files in the dev tools:
Secret scanning tools are tools that scan Git commits in remote repositories whether on GitHub, GitLab, or Bitbucket to check for secrets that were accidentally committed. This helps to prevent sensitive information from being exposed to remote repositories. With these solutions, secrets committed to the repo are automatically detected and captured.
To set up GitGuardian for your project:
Do not share GitHub credentials with anyone outside of your development team and make sure to revoke access for developers who no longer work on your team.
Securing API keys and secrets is very important in your frontend application. Storing secrets in a .env
file is good, but that alone is not safe. Always make sure to set restrictions on your key. With this, even though your secret is leaked, it will be useless in the hands of whoever has access to it.
An extra layer of security is using secret scanning services to scan your repositories. Use the practices highlighted in this article to ensure sensitive data is protected while working on projects. Thanks for reading.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
3 Replies to "Best practices for managing and storing secrets in frontend development"
This information is helpful (especially the things *not* to do), but it still does not provide enough advice on what *to* do to avoid exposing secrets in your downloaded application code – even if they are configured with environment variables.
An approach I’m using — in an application that involves some sort of user authentication mechanism — is to store each user’s secrets in the server side user information database, and provide an API call that allows an authenticated user to download them. (Obviously, use SSL/TLS on this API call!) This approach avoids the problems of embedding secrets, and (if they are truly user-specific, like Gmail credentials) allows the users to manage them just like they manage the rest of their profile. If the secrets are global to the application (i.e. your organization signed up for them on behalf of all the users), still provide the API call, but never expose the secrets in the UI anywhere.
This is still not a perfect defense against someone armed with the ability to do client side debugging in the browser, but makes life significantly more difficult for them.
Having secrets in the frontend code is **always** a bad practice. You can obscure them using some techniques but I think it will create a false sense of security. I.E.: Any API key you have in your code will be sent clearly in the request to the corresponding service, no matter how hidden it’s in the code.
Any reputable service will always have both private and public API keys if necessary.
Another topic would be that you want to parametrize secrets. In fact, it is recommended, but it must not be confused with a security measure.
You can use a middleware service to help you store and secure your API secrets then proxy the API calls to your frontend. This is the best way of going about it if you don’t want to set up your own backend server. We used a platform called KOR Connect, they simplified the whole connection into a couple simple steps as well as created their own layers of security on top of the regular proxy. Might be worth looking into?