Engineering Deep Dive

Firestore Client Side Encryption: A Secure Field-Level Guide

Secure your sensitive database records before they leave the browser. Learn how to implement robust firestore client side encryption using the native Web Crypto API.

Krapton Engineering
Reviewed by a senior engineer5 min read
Share
Firestore Client Side Encryption: A Secure Field-Level Guide

Storing highly sensitive user data like medical histories, financial records, or private API keys directly in cloud databases is a significant liability. Even with strict server-side configurations, database administrators or compromised credentials can expose plain-text records. Implementing robust firestore client side encryption ensures that sensitive fields are encrypted directly in the user's browser before ever touching Google's infrastructure.

TL;DR: To secure sensitive document fields in Firestore, do not rely on weak third-party JS libraries or static keys. Instead, use the native browser Web Crypto API to derive unique keys per user (using PBKDF2) and encrypt fields using AES-GCM 256-bit encryption before writing to the database.

Key takeaways

Side profile of a man with eyeglasses and green binary code projected on face.
Photo by cottonbro studio on Pexels
  • Zero-Knowledge Architecture: Firebase never sees the raw decryption keys, ensuring complete data privacy.
  • Native Performance: Utilizing the browser's hardware-accelerated Web Crypto API prevents UI blocking during cryptographic operations.
  • Field-Level Granularity: Encrypt only specific PII fields (like "ssn" or "medical_history") while keeping indexing fields unencrypted.
  • Key Management: Derive cryptographic keys using a combination of the user's authentication context and a secure, client-side salt.

The Vulnerability of Standard Firestore Architectures

Abstract green matrix code background with binary style.
Photo by Markus Spiske on Pexels

By default, Cloud Firestore encrypts data at rest and in transit. However, this is server-side encryption. Google manages the keys, and anyone with Viewer or Editor IAM permissions in your Google Cloud Console can read the plain-text data. If a malicious actor compromises your Firebase project console, your users' private data is fully exposed.

To mitigate this, true zero-trust applications employ firestore client side encryption. In this architecture, encryption occurs on the client device, and the ciphertext is sent to Firestore. The decryption key is derived from the user's credentials and is never stored on the server.

The Naive Approach (And Why It Fails)

Many developers start by importing lightweight libraries like crypto-js and encrypting fields using the user's Firebase UID as the encryption key. In a recent client engagement, we audited an application utilizing this exact pattern. The failure modes were immediate and severe:

  1. Static Key Vulnerability: The Firebase UID is not a secret. It is often exposed in client-side client lists, public profiles, and request metadata. Using it as an encryption key is equivalent to obfuscation, not encryption.
  2. Performance Bottlenecks: On a production rollout we shipped, we observed that using pure JavaScript cryptographic libraries caused blocking main-thread execution on larger payloads, dropping frame rates during database writes.
  3. Lack of Authenticated Encryption: Naive implementations often use AES-CBC without an integrity check, making the ciphertext vulnerable to padding oracle attacks.

The Production-Grade Solution: Web Crypto API

To implement secure, hardware-accelerated cryptography, we use the native Web Crypto API. It runs securely in the browser sandbox and leverages OS-level optimizations. Below is a production-ready TypeScript module to securely encrypt and decrypt fields.

// cryptoService.ts
const ALGORITHM = 'AES-GCM';
const KEY_LENGTH = 256;

async function deriveKey(passphrase: string, saltStr: string): Promise<CryptoKey> {
  const encoder = new TextEncoder();
  const baseKey = await window.crypto.subtle.importKey(
    'raw',
    encoder.encode(passphrase),
    'PBKDF2',
    false,
    ['deriveKey']
  );

  return window.crypto.subtle.deriveKey(
    {
      name: 'PBKDF2',
      salt: encoder.encode(saltStr),
      iterations: 100000,
      hash: 'SHA-256'
    },
    baseKey,
    { name: ALGORITHM, length: KEY_LENGTH },
    false,
    ['encrypt', 'decrypt']
  );
}

export async function encryptField(plainText: string, secret: string, salt: string): Promise<string> {
  const key = await deriveKey(secret, salt);
  const iv = window.crypto.getRandomValues(new Uint8Array(12));
  const encoder = new TextEncoder();
  
  const encrypted = await window.crypto.subtle.encrypt(
    { name: ALGORITHM, iv },
    key,
    encoder.encode(plainText)
  );

  const combined = new Uint8Array(iv.length + encrypted.byteLength);
  combined.set(iv, 0);
  combined.set(new Uint8Array(encrypted), iv.length);

  return btoa(String.fromCharCode(...combined));
}

export async function decryptField(cipherTextBase64: string, secret: string, salt: string): Promise<string> {
  const key = await deriveKey(secret, salt);
  const combined = new Uint8Array(atob(cipherTextBase64).split('').map(c => c.charCodeAt(0)));
  
  const iv = combined.slice(0, 12);
  const data = combined.slice(12);

  const decrypted = await window.crypto.subtle.decrypt(
    { name: ALGORITHM, iv },
    key,
    data
  );

  return new TextDecoder().decode(decrypted);
}

Architecting the Database Layout

When you encrypt firestore data client side, you lose the ability to query or filter by those fields on the server. Therefore, you must carefully separate indexable fields from encrypted fields. The table below outlines the ideal schema split for a user profile document.

Field Name Type Encrypted? Purpose
uid String No Document indexing & querying
role String No Used by Firebase Security Rules
ssn_encrypted Base64 String Yes Highly sensitive personal identifier
medical_notes_encrypted Base64 String Yes Private clinical diagnostic notes

When NOT to use client side encryption

While client side encryption firebase patterns guarantee maximum privacy, they introduce structural limitations. If your business logic relies heavily on server-side full-text search, complex range queries on the encrypted fields, or third-party cloud integrations that need to parse your data, client-side encryption will block these workflows. In those scenarios, server-side KMS-managed encryption is preferred.

Integrating Encryption with React Context

To make this seamless for developers, wrap the encryption state within a React Context. This ensures that the derived key stays in memory and is automatically cleared when the user logs out or closes the session.

If you need to scale this architecture globally, optimize your key cycles, or implement secure multi-party decryption, you may want to consult with experienced professionals. Our team provides dedicated software security services to help startups and enterprises build zero-trust cloud architectures.

FAQ

Can I query encrypted Firestore fields?

No. Because client side encryption firebase models produce unique ciphertexts (due to randomized Initialization Vectors), you cannot perform exact matches or range queries on the server. If querying is required, you must use deterministic hashing (with caution) or keep indexing tokens unencrypted.

What happens if the user forgets their password/secret?

If the user's password is the sole source of the encryption key, and they lose it, the encrypted data is permanently unrecoverable. To mitigate this, implement a secure recovery key escrow system or utilize split-key sharing techniques during onboarding.

Does this replace Firebase Security Rules?

No. Client-side encryption protects data confidentiality, but Firebase Security Rules are still required to control authorization, write access, and overall database integrity.

Next Steps: Secure Your Application Today

Implementing bulletproof firestore client side encryption requires deep cryptographic knowledge, careful key derivation, and a solid understanding of browser performance bottlenecks. Don't risk data leaks or architectural dead-ends. If you need this shipped securely to production, hire a dedicated Krapton team of veteran software engineers to design and implement your zero-trust data strategy.

About the author

Krapton's engineering group specializes in building highly secure cloud platforms and custom software architectures. We have shipped enterprise-grade, compliant web and mobile apps for clients worldwide, ensuring zero-trust security standards since inception.

firebasefirestoresecurityweb-cryptoreacttypescript
About the author

Krapton Engineering

Krapton's core engineering team builds secure, high-performance cloud architectures, specializing in zero-trust data strategies and Web Crypto integrations for global enterprises.