Chrome Extensions

How to Store Data Securely in a Chrome Extension

Extensions often need to store something: settings, tokens, user data. The easy path is to dump it in localStorage and move on, but extensions handle sensitive data and run in a shared browser, so where and how you store matters more than in a normal web app. Here is how I keep Chrome extension storage secure, and what never belongs in it.

Illustration of a Chrome extension popup open in the browser, How to Store Data Securely in a Chrome Extension
How to keep Chrome extension storage secure: which storage area to use, why API keys never belong in the bundle, and a checklist to run…

Use chrome.storage

chrome.storage is the extension-native option: accessible from the background worker and popup, able to sync across the user’s devices if you want, and the correct home for extension data. Plain localStorage is tied to a single context and isn’t the right tool here. It is not even available in the background service worker, which is where much of an extension’s logic runs.

There are three storage areas, and choosing the right one is the first security decision:

  • chrome.storage.local is saved on the device and survives restarts. It suits most settings and cached data.
  • chrome.storage.sync follows the user across their signed-in browsers, with tight size limits. Keep it for preferences, never for tokens.
  • chrome.storage.session is held in memory and cleared when the browser closes. By default content scripts cannot read it, which makes it the better place for short-lived sensitive values.

One point people miss: the chrome.storage documentation states that the storage area is not encrypted. It is the right place for extension data, but it is not a vault. Anything stored there should be something you could live with being read by someone who has the device.

Never ship secrets you don’t have to

The biggest mistake is bundling an API key inside the extension. Anyone can unpack an extension and read its code, so a bundled key is a public key. If your extension talks to a paid API, route those calls through your own backend that holds the key, don’t embed it.

That backend can be small: one endpoint that checks who is calling, applies a rate limit, and forwards the request with the real key attached. If the extension pairs with your business website, that site is often the natural home for it. A WordPress site can expose it through its REST API, while a hosted site builder may not let you run server code at all, which is one of the trade-offs I cover in WordPress vs Webflow for a small business.

Treat user tokens carefully: scope them tightly and expire them, because a token that lives forever in storage is a standing risk if the device is compromised. And request the minimum permissions. Every permission is attack surface and a reason for users and reviewers to distrust the extension.

Handle tokens like they will leak

Use short-lived access tokens and refresh them through your backend. For sign-in with a third-party provider, chrome.identity handles the OAuth flow so you never see the user’s password. When the user logs out, remove their tokens and cached data from storage straight away, and never write a token to the console, even while debugging.

A security checklist for Chrome extension storage

Run through this before every release. Each item is something a reviewer or an attacker can check in minutes.

  1. Search the built bundle for keys. Unpack your own release and search for anything that looks like an API key or secret.
  2. List what you store and why. If you cannot say why a value is kept, delete it.
  3. Keep sensitive values out of content scripts. Content scripts run inside web pages you do not control. Let the background worker hold tokens and do the authenticated calls.
  4. Validate every message. Treat messages from content scripts as untrusted input, and check the sender and the data before acting on them.
  5. Trim permissions. Move rarely used ones to optional permissions requested at the moment they are needed. The Chrome guide to declaring permissions explains how.
  6. Test logout. After logging out, open the storage viewer in DevTools and confirm nothing personal is left behind.
Infographic summarising “How to Store Data Securely in a Chrome Extension”: Use chrome.storage; Never ship secrets you don’t have to; A security checklist for Chrome extension storage; Common storage mistakes I see; The short version
The article at a glance.

Common storage mistakes I see

  • Treating obfuscation as protection. Minifying or encoding a key does not hide it. Anyone patient can recover it.
  • Storing full user records “just in case”. Cache only what the interface needs, and fetch the rest when required.
  • Keeping cached data forever. Give cached personal data an expiry time and clear old entries on a schedule with chrome.alarms, so it does not quietly pile up on the device for years.
  • Putting tokens in sync storage. That copies them to every browser the user signs into.
  • Trusting the page. Data read from a website’s DOM can be manipulated by that website, so check it before storing it.

The short version

Store data in chrome.storage, keep real secrets on your server, scope and expire tokens, and ask for as little access as possible.

Storage is only one part of getting through review. Broad permissions and remote code are the other usual problems, covered in common Manifest V3 mistakes that get extensions rejected. If you are building the interface in React, building a Chrome extension with React shows how storage fits into the popup. And if you would like the extension and its backend built properly from the start, that is part of my web application development work.

Questions people ask

Is chrome.storage encrypted?

No. Chrome's own documentation says the storage area is not encrypted. It is the right place for extension settings and cached data, but confidential information such as long-lived secrets should not be kept there. Keep real secrets on your server and store only short-lived tokens in the extension.

Should I use localStorage or chrome.storage in an extension?

Use chrome.storage. It works from the popup, options page and background service worker, and can sync settings across the user's browsers. localStorage is tied to a single context and is not available in the background service worker at all.

How do I hide an API key in a Chrome extension?

You cannot. Anyone can unpack an extension and read its code, so a bundled key is effectively public, even if it is minified or encoded. Route calls to paid APIs through your own backend, which holds the key and forwards the request after checking who is calling.

Where should a Chrome extension keep login tokens?

Keep them in the background service worker's storage rather than anywhere content scripts can reach, use short-lived tokens refreshed through your backend, and remove them as soon as the user logs out. Session storage suits short-lived values because it is cleared when the browser closes.

Comments

Leave a comment

0 / 3000
Comments are moderated. Links are not clickable and your email is never published. See the privacy policy.
keep reading

Related articles

Usually replies within a day
let’s talk

Tell me what you want to build

A website, an app, some automation, or something you’re still figuring out. Tell me the problem and I’ll give you a straight answer on what it takes. No pressure, no sales team.