Patience Adajah Software developer and technical writer.

Best practices for managing and storing secrets in frontend development

6 min read 1691

Best practices for managing and storing secrets in frontend development

Secrets in frontend development

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 APIs. A private API is an API that is developed and hosted by in-house developers within an organization. Private APIs are only available to in-house developers and are not shared or used by developers outside that organization. Private APIs are easily managed as the developers have full control of how applications are developed with it. A private API is basically an API that allows you to interface with an application feature that is built by developers in a particular organization. A good example of a private API is the 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 an API service offered by a third party that is publicly available that 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 leverage of 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 application. 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 as it could pose serious challenges if it gets into the wrong hands.

Potential problems that may occur as a result of exposed secrets

API keys and credential secrets that are not properly stored could cause damages which could be financial, regulatory, or reputational.

  • In cases where a third-party service provider like Google Cloud Platform(GCP) offers access to their service for a limited rate, if your secret gets exposed, you may be denied access to the service because the unauthorized user has performed so many requests on your behalf thereby exceeding your limit. Apart from just exceeding your limit, the bills may spike up
  • If your credential secret is leaked and your application violates the API provider terms of use, the API provider may withdraw your access to their service and this may hurt your organization’s reputation
  • Finally, you lose control of your resource, as the hacker may be able to instruct the provider directly and bypass your business logic
  • The hacker may gain access to sensitive data which may cause a breach in your organization’s data

Bad practices

Embedding your credential secrets directly in your code

I will be using React in the following code snippet but it 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. This can be done simply:

  • Inspect the webpage or CTRL + SHIFT + I
  • Go to “Sources” tab
  • Click on static/js
  • Click on main.chunk.js

You will find your credential secret, pretty easy for anyone to extract:

secret found in inspected site

Uploading your codebase to git/GitHub with your secret directly in your code

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 though your repository is a private repository, 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 remove all traces of it by using git rebase to remove the particular commit that added the keys.

We made a custom demo for .
No really. Click here to check it out.

Not setting restrictions on your API key or secret

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 the API can be accessed from. From the image below, no domain was saved, so requests can be sent from any URL with the API credentials.

no domain was saved

Good practices

Set restrictions on the API key

Some service providers allow you to set restrictions on the usage of the API key, so 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 as it can only be used with the specified URL. Also, you can set a daily limit on the usage of your API credentials. In this image below, requests to the API can only be made to the URL I specified.

domain restrictions set

Conceal your keys in an environment variable (.env) file

With 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:

  • Create a .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
  • In the .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 as your API key is still visible in the browser devtool. 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:

  • Inspect the webpage or CTRL + SHIFT + I
  • Go to “Sources” tab
  • Click on static/js
  • Click on main.chunk.js

You will find the API keys defined in the .env files in the devtools.

env files in devtools

Scan git repositories with secret scanning solutions such as GitGuardian

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:

  • Select a suitable plan and create an account on GitGuardian
  • Confirm your email and log into your dashboard
  • On your dashboard, go to Integrations ⇒ Source monitoring ⇒ Install(to your web-based repo – GitHub, GitLab, Github Enterprise)
  • Once you have installed it, select the project from your repos
  • GitGuardian scans the repo and sends you a mail informing you about possible secret leakage

Do not share GitHub credentials

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.

Conclusion

Securing API keys and secrets is very important in your frontend application, depending on the level of security you want for your API credential. 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.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Patience Adajah Software developer and technical writer.

3 Replies to “Best practices for managing and storing secrets in frontend…”

  1. 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.

  2. 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.

  3. 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?

Leave a Reply