Chrome Local Storage & IndexedDB (LevelDB)
2026-05-19 · 2 min
Beyond the SQLite files, Chrome keeps web-app state in LevelDB key-value
stores. Most investigators skip them and miss the richest source of
intent evidence on the machine. A logged-in identity, a draft message,
an OAuth token, the URL of a Drive document the user opened — that data
lives in Local Storage or IndexedDB, not in History.
Where it lives in the profile
Local Storage/leveldb/ # window.localStorage per origin
Session Storage/ # per-tab session storage
IndexedDB/<origin>.indexeddb.leveldb/ # structured app databases
IndexedDB/<origin>.indexeddb.blob/ # large blobs referenced by the LDB
Each entry is a folder, not a single file: CURRENT, MANIFEST-NNNNNN,
one or more *.ldb SSTables, and a *.log write-ahead log. Copy the
whole directory or LevelDB will refuse to open it.
The format you actually have to read
LevelDB is an LSM tree. To enumerate keys correctly:
- Read
CURRENTto find the active manifest. - Walk
MANIFEST-*to learn which SSTables are live at which level. - Merge the live SSTables with the
*.logWAL, newest write wins per key. - For each block, decompress Snappy if the trailer says so.
The values then need format-specific decoding:
- Local Storage keys are
_<origin>\x00<key>. Values start with a one-byte encoding marker:0x00UTF-16LE,0x01Latin-1, sometimes preceded by a meta byte. - Session Storage uses a
namespace -> map -> keyindirection. Resolve via the meta-namespace keys before extracting values. - IndexedDB values are V8 structured-clone encoded. Numbers, strings and simple objects round-trip cleanly; cyclic refs and typed arrays need careful handling.
Real evidence you find here
- Slack, Teams, Discord, Notion: logged-in user IDs, workspace IDs, recently-opened channels, draft messages.
- Google Docs / Drive: file IDs (the long random part of a Drive URL), recently-edited document titles, sync state.
- VS Code Web / GitHub Codespaces: open file paths, repo names.
- Banking and SSO: JWT tokens with claims still parseable (audience, issuer, expiry, sometimes username).
- Crypto wallets: stored addresses, often the wallet identifier.
Caveats
- The WAL (
*.log) is where the most recent writes live. If you copy only the*.ldbfiles, you lose the last minutes of activity. - LevelDB compaction can produce tombstones for deleted keys that still surface during merge. Treat unexplained NULs in your output as deletes, not data.
- Browsers occasionally migrate origins between LevelDB stores. A clean shutdown is friendlier; a crash mid-migration can leave orphaned data worth recovering by hand.