How HexVault protects
your passwords
A complete technical account of our encryption architecture. We believe security should be verifiable — not a marketing claim. Everything on this page is accurate to the code we ship, and you can inspect it yourself.
End-to-end in three steps
HexVault's security model is simple enough to explain in three steps and strong enough to withstand a server breach.
non-extractable by the Web Crypto API, meaning no JavaScript can read it back out once created.base64(ciphertext) and base64(IV). It has no access to your master password, encryption key, or plaintext passwords — ever. A full database dump yields only ciphertext that is computationally infeasible to decrypt without your master password.Argon2id — why we chose it over PBKDF2
Your master password passes through Argon2id — winner of the 2015 Password Hashing Competition, designed specifically to resist GPU and ASIC brute-force attacks.
| Parameter | Value | Why |
|---|---|---|
| Algorithm | Argon2id | Hybrid: side-channel resistant + GPU resistant |
| Memory cost | 65,536 KiB (64 MB) | OWASP 2023 minimum for interactive logins |
| Time cost | 3 iterations | ~500 ms on modern hardware |
| Parallelism | 4 threads | Matches typical device core count |
| Output | 32 bytes (256 bits) | Exact key material for AES-256 |
| Salt | 256-bit random, per user | Prevents precomputation; unique per account |
async function deriveKey(masterPassword, vaultSalt) { const salt = Uint8Array.from(atob(vaultSalt), c => c.charCodeAt(0)); const result = await argon2.hash({ pass: masterPassword, salt, type: argon2.ArgonType.Argon2id, mem: 65536, time: 3, parallelism: 4, hashLen: 32, }); return await crypto.subtle.importKey( 'raw', result.hash, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt'] ); }
AES-256-GCM with a unique IV per entry
Every password is encrypted independently with a fresh random 96-bit IV. Reusing an IV with the same key would be catastrophic — so we never do it.
| Property | Value | Notes |
|---|---|---|
| Mode | AES-256-GCM | Authenticated encryption — tampering is detected and rejected |
| Key length | 256 bits | Maximum AES key size; 2²⁵⁶ keyspace |
| IV | 96 bits, random per entry | Via crypto.getRandomValues(); stored alongside ciphertext |
| Auth tag | 128 bits | Cryptographic proof ciphertext has not been modified |
| Crypto API | Web Crypto (browser-native) | OS-level; no third-party crypto library |
k-Anonymity — your passwords never leave your device
HexVault checks against 12 billion+ compromised credentials via Have I Been Pwned. Your actual passwords are never transmitted — only a partial hash.
HexVault computes a SHA-1 hash of your password entirely in your browser. Only the first five characters of that hash are sent to HIBP. The server responds with every matching suffix (~500 results). HexVault checks locally whether your full hash is in that list.
HIBP receives 5 hex characters out of 40 — 1/2²⁰ of the full hash. That is mathematically insufficient to determine which password you queried.
How password sharing works
Two sharing models — user-to-user and one-time secure links — both preserving zero-knowledge within the constraints of shared secrets.
Encrypted in transit, hardened at rest
TLS 1.3 via Cloudflare on all connections. Full security controls applied to every request.
| Control | Implementation |
|---|---|
| TLS | 1.3 minimum · TLS 1.0/1.1 disabled via Cloudflare |
| HSTS | max-age=31536000; includeSubDomains |
| CSP | script-src 'self' 'wasm-unsafe-eval' — no unsafe-inline |
| Database | PostgreSQL · vault entries stored as base64 AES-256-GCM ciphertext |
| Session cookies | HttpOnly · Secure · SameSite=Lax · auto-expire on inactivity |
| Auth passwords | bcrypt with per-user salt · separate from vault encryption |
| Rate limiting | Login: 10 attempts / 15 min · account lockout after threshold |
| Two-factor auth | TOTP (RFC 6238) · WebAuthn / FIDO2 hardware keys |
| CSRF | Per-session token on all state-changing requests |
| Data jurisdiction | UK-based · GDPR compliant |
TOTP, WebAuthn, and replay protection
HexVault supports two independent 2FA mechanisms, both with active replay attack prevention.
| Property | TOTP | WebAuthn |
|---|---|---|
| Standard | RFC 6238 / HOTP | FIDO2 / W3C WebAuthn |
| Replay protection | Code + timestamp logged server-side | Challenge-response — no reusable secrets |
| Phishing resistance | Partial (code can be relayed) | Full — origin-bound credential |
| Stored server-side | Encrypted TOTP secret | Public key only |
How sessions are protected
HexVault uses server-side sessions with multiple layers of protection against hijacking and fixation.
| Control | Implementation | Why it matters |
|---|---|---|
| Cookie flags | HttpOnly; Secure; SameSite=Lax | Prevents JS access and CSRF; HTTPS-only transmission |
| Session storage | Server-side (Redis) | Session data never sent to client — token only |
| CSRF tokens | Per-session, checked on all state-changing requests | Prevents cross-origin forged requests |
| IP binding | Optional — triggers re-auth on IP change | Limits stolen-cookie utility |
| Inactivity timeout | Configurable 15–480 min auto-lock | Limits exposure window on unattended devices |
| Concurrent sessions | Tracked per device — any can be revoked | User can terminate suspicious sessions immediately |
| Vault key in memory | Non-extractable CryptoKey; erased on logout/lock | Closing the tab destroys the key — ciphertext at rest is safe |
How team and enterprise vaults maintain separation
Org vaults use a symmetric group key distributed via ECDH key agreement — no member's personal vault key is ever exposed to the organisation.
Upgrading legacy password hashes transparently
Early accounts used PBKDF2-SHA256 for auth password hashing. All accounts transparently migrate to bcrypt (cost 12) on next login.
On successful login, HexVault detects the hash algorithm used. If it is PBKDF2, the auth password is re-hashed with bcrypt cost-factor 12 and the stored hash is updated atomically. The user notices nothing. After migration, login verifies against bcrypt only.
Note that auth password hashing is entirely separate from vault encryption. Your vault remains AES-256-GCM regardless of which auth hash algorithm is in use.
| Algorithm | When used | Status |
|---|---|---|
| PBKDF2-SHA256 | Accounts created before v6.39.51 | Auto-migrates on login |
| bcrypt cost=12 | All accounts after migration | Current standard |
Don't take our word for it
The functions on this page are the actual functions running in your browser. We don't minify our client-side code — inspect it at any time in DevTools.
encrypted_password and iv — never plaintext.How we compare
Argon2id's memory-hardness changes the economics of brute-force attacks fundamentally versus the PBKDF2 used by competitors.
| Feature | HexVault | 1Password | Bitwarden | LastPass |
|---|---|---|---|---|
| Key derivation | Argon2id | PBKDF2 | Argon2id / PBKDF2 | PBKDF2 |
| GPU-resistant KDF | Yes | No | Optional | No |
| Memory-hard (64 MB default) | Yes | No | 19 MB default | No |
| Cipher | AES-256-GCM | AES-256-GCM | AES-256-GCM | AES-256-GCM |
| Zero-knowledge | Yes | Yes | Yes | Partial |
| AI security assistant | HexGuard | No | No | No |
| Auditable client code | Unminified | No | Partial | No |
| Jurisdiction | UK | Canada | US | US |
| Public security docs | This page | Partial | Partial | No |
Found a vulnerability?
We respond quickly to credible security findings. Please give us the opportunity to fix an issue before making it public.
[email protected] with description and reproduction steps. We acknowledge within 48 hours and aim to patch critical issues within 7 days.