WSTG - Latest

Testing Browser Storage

ID          
WSTG-CLNT-12

Summary

Browsers provide the following client-side storage mechanisms for developers to store and retrieve data:

  • LocalStorage
  • SessionStorage
  • IndexedDB
  • Web SQL (Deprecated)
  • Cookies

These storage mechanisms can be viewed and edited using the browser’s developer tools, such as Google Chrome DevTools or Firefox’s Storage Inspector.

Note: While cache is also a form of storage it is covered in a separate section covering its own peculiarities and concerns.

Test Objectives

  • Determine whether the site is storing sensitive data in client-side storage.
  • The code handling of the storage objects should be examined for possibilities of injection attacks, such as utilizing unvalidated input or vulnerable libraries.

How to Test

LocalStorage

window.localStorage is a global property that implements the Web Storage API and provides persistent key-value storage in the browser.

Both the keys and values can only be strings, so any non-string values must be converted to strings first before storing them, usually done via JSON.stringify.

Entries to localStorage persist even when the browser window closes, with the exception of windows in Private/Incognito mode.

The maximum storage capacity of localStorage varies between browsers.

List All Key-Value Entries

for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i);
  const value = localStorage.getItem(key);
  console.log(`${key}: ${value}`);
}

SessionStorage

window.sessionStorage is a global property that implements the Web Storage API and provides ephemeral key-value storage in the browser.

Both the keys and values can only be strings, so any non-string values must be converted to strings first before storing them, usually done via JSON.stringify.

Entries to sessionStorage are ephemeral because they are cleared when the browser tab/window is closed.

The maximum storage capacity of sessionStorage varies between browsers.

List All Key-Value Entries

for (let i = 0; i < sessionStorage.length; i++) {
  const key = sessionStorage.key(i);
  const value = sessionStorage.getItem(key);
  console.log(`${key}: ${value}`);
}

IndexedDB

IndexedDB is a transactional, object-oriented database intended for structured data. An IndexedDB database can have multiple object stores and each object store can have multiple objects.

In contrast to localStorage and sessionStorage, IndexedDB can store more than just strings. Any objects supported by the structured clone algorithm can be stored in IndexedDB.

An example of a complex JavaScript object that can be stored in IndexedDB, but not in localStorage/sessionStorage are CryptoKeys.

W3C recommendation on Web Crypto API recommends that CryptoKeys that need to be persisted in the browser, to be stored in IndexedDB. When testing a web page, look for any CryptoKeys in IndexedDB and check if they are set as extractable: true when they should have been set to extractable: false (i.e. ensure the underlying private key material is never exposed during cryptographic operations.)

const dumpIndexedDB = dbName => {
  const DB_VERSION = 1;
  const req = indexedDB.open(dbName, DB_VERSION);
  req.onsuccess = function() {
    const db = req.result;
    const objectStoreNames = db.objectStoreNames || [];

    console.log(`[*] Database: ${dbName}`);

    Array.from(objectStoreNames).forEach(storeName => {
      const txn = db.transaction(storeName, 'readonly');
      const objectStore = txn.objectStore(storeName);

      console.log(`\t[+] ObjectStore: ${storeName}`);

      // Print all entries in objectStore with name `storeName`
      objectStore.getAll().onsuccess = event => {
        const items = event.target.result || [];
        items.forEach(item => console.log(`\t\t[-] `, item));
      };
    });
  };
};

indexedDB.databases().then(dbs => dbs.forEach(db => dumpIndexedDB(db.name)));

Web SQL

Web SQL is deprecated since November 18, 2010 and it’s recommended that web developers do not use it.

Cookies

Cookies are a key-value storage mechanism that is primarily used for session management but web developers can still use it to store arbitrary string data.

Cookies are covered extensively in the testing for Cookies attributes scenario.

List All Cookies

console.log(window.document.cookie);

Global Window Object

Sometimes web developers initialize and maintain global state that is available only during the runtime life of the page by assigning custom attributes to the global window object. For example:

window.MY_STATE = {
  counter: 0,
  flag: false,
};

Any data attached on the window object will be lost when the page is refreshed or closed.

List All Entries on the Window Object

(() => {
  // create an iframe and append to body to load a clean window object
  const iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  document.body.appendChild(iframe);

  // get the current list of properties on window
  const currentWindow = Object.getOwnPropertyNames(window);

  // filter the list against the properties that exist in the clean window
  const results = currentWindow.filter(
    prop => !iframe.contentWindow.hasOwnProperty(prop)
  );

  // remove iframe
  document.body.removeChild(iframe);

  // log key-value entries that are different
  results.forEach(key => console.log(`${key}: ${window[key]}`));
})();

(Modified version of this snippet)

Security Implications

When reviewing browser storage mechanisms, testers should evaluate whether sensitive data is unnecessarily exposed on the client-side. Modern applications, especially SPAs, frequently store authentication tokens or application state in browser storage, which may introduce security risks.

Common concerns include:

  • Authentication tokens (e.g., JWTs) stored in localStorage or sessionStorage, which are accessible via JavaScript and may be exposed through XSS.
  • Tokens or session identifiers persisting after logout.
  • Sensitive business data stored in IndexedDB or localStorage without a clear requirement.
  • Cryptographic material stored as extractable when it should be protected.

Improper client-side storage may increase the impact of client-side attacks such as DOM-based XSS.

General Testing Guidance

In addition to enumerating storage entries, testers should:

  • Inspect browser storage using developer tools (Application/Storage tab).
  • Identify authentication tokens, session identifiers, or sensitive business data.
  • Attempt to access stored values via the JavaScript console.
  • Verify whether storage entries are cleared after logout or session expiration.
  • Assess whether stored data could be leveraged in a client-side attack chain.

Attack Chain

Following the identification of any of the above attack vectors, an attack chain can be formed with different types of client-side attacks, such as DOM based XSS attacks.

Remediation

Applications should be storing sensitive data on the server-side, and not on the client-side, in a secured manner following best practices.

References

For more OWASP resources on the HTML5 Web Storage API, see the Session Management Cheat Sheet.