You can’t keep telling your board that “encryption at rest” is enough. Your lawyers know better, your customers assume better, and every engineer with psql access can still SELECT the most sensitive data in your system. Yet you still need to search it: find a user by email, dedupe phone numbers, filter by birthdate range. Fully homomorphic crypto won’t save you in 2026. Practical searchable encryption will — if you pick the right patterns, accept the right leakages, and operationalize it.
What Just Changed: Searchable Field-Level Encryption Went from Research to Roadmap
Last week’s buzz around Supabase adding field-level encrypted search via CipherStash is more than a product update — it’s the signal that searchable encryption is finally crossing the chasm. You don’t have to run a bespoke crypto service to make equality and range queries work on encrypted data anymore. And you don’t have to pick between “nobody can query anything” and “DBAs can read everything.”
The catch: you must be explicit about which queries you’ll support, what you’re willing to leak (order, frequency, access patterns), and where keys live. Pretend these trade-offs don’t exist and you’ll either blow up performance or hand attackers a side channel they can exploit.
Start with a Threat Model, Not a Library
If you don’t write down who you’re protecting against, you’ll over- or under-engineer this. Here’s the fast scoring framework we use with CTOs:
Adversaries you likely care about
- Cloud insiders and DB snapshots: Managed Postgres backups, database logs, support access. Encrypting at rest protects disks, not operators.
- Exfil via SQL injection or misconfigured read replica: Automated attackers who can dump tables but can’t run your app code.
- Legal requests or data residency drift: You may need to prove that production staff (or even your cloud) cannot decrypt certain fields.
Adversaries you probably won’t win against with crypto alone
- Fully compromised app tier: If an attacker owns the process that holds keys, they can decrypt. Compensate with rate limits, anomaly detection, and short-lived keys, but assume decryption is possible.
- Perfectly hidden access patterns: Oblivious RAM still isn’t practical for your SaaS. Accept that access patterns leak without heavy cost.
Translate that into a policy: “Protect PII from database and cloud operators; tolerate leakage of query patterns; assume app compromise equals decryption; encrypt field-by-field with keys the DB never sees.”
The Decision Framework: Pick Techniques Per Query, Not Per Table
Searchable encryption is a bag of techniques. Use the lightest tool that delivers your query semantics and threat model.
1) Map Your Query Patterns
- Equality: Find by email, phone, SSN/CPF/CNPJ, invoice ID.
- Prefix: Search email by username or phone by country code + area code.
- Range/Sort: Birthdate ranges, order amounts between X and Y, sort by last_activity_at.
- Fuzzy/Text: Name search, address contains substring.
Then label sensitivity. Email, phone, SSN/CPF are high sensitivity. Zip code, city, or state are lower (but still PII in many regimes). Don’t waste cryptographic complexity on non-sensitive aggregates.
2) Choose the Right Primitive per Field
These are the building blocks that actually ship in 2026:
- Deterministic Encryption (DE) for equality: Canonicalize input (lowercase email, E.164 for phone), then encrypt with a deterministic mode (e.g., AES-SIV). Same plaintext → same ciphertext, enabling equality comparisons and hashed indexes. Leakage: frequency (same values produce same ciphertext), equality patterns across rows.
- Blind Indexes for equality and prefix: Compute an HMAC with a secret salt over the canonical value (or its n-grams for prefix search) and index those tokens. Store only tokens in the DB, not plaintext or salts. Leakage: token equality and token counts; mitigated by per-tenant salts and pepper in a KMS.
- Order-Revealing or Bucketized Range: True order-revealing encryption exists, but leakage can be ugly. A pragmatic pattern is bucketization: map a numeric/temporal field to buckets (e.g., month, $10 ranges) and index bucket tokens. Coarser granularity → less leakage and smaller indexes, but more false positives you filter in the app.
- Client- or service-side Encrypted Search for text: If you need full-text search over sensitive fields, accept that you’ll push indexing to a client or a dedicated encrypted search service that holds keys application-side (CipherStash is one option). Trying to bolt fuzzy text search onto Postgres with cryptographic guarantees is how projects die.
Reality check: 80% of sensitive-field queries in B2B SaaS are equality or coarse range. You can secure those today without moon math.
3) Key Management That Doesn’t Torpedo Latency
- Envelope keys per tenant: Generate a Data Encryption Key (DEK) per tenant or dataset. Encrypt DEKs with a Key Encryption Key (KEK) in a managed KMS (AWS KMS, GCP KMS). Store the encrypted DEK alongside tenant metadata.
- Warm the cache: On app start or tenant access, decrypt the DEK via KMS once and keep it in memory with a short TTL (e.g., 10–15 minutes). Expect ~1–3 ms per KMS decrypt when warm; don’t do this per row.
- Rotate without downtime: Version DEKs. On write, use DEK v2; on read, try v2 then fall back to v1. Backfill in batches.
- Split trust: Keep salts/peppers for blind indexes in KMS or an HSM-backed service; never store them in the DB.
4) Performance and Storage Math (So You Don’t Guess)
- Equality with DE or blind index: Storage overhead ~1.1–1.3x per field (ciphertext + token). Query latency overhead is dominated by network/IO; expect +5–15 ms p95 over a typical indexed lookup, driven by app-layer tokenization and post-filtering.
- Prefix with n-grams: If you index 3-grams for email local-parts, you’ll store ~L-2 tokens per value (L = characters in the prefixable segment). Index bloat of 2–4x is common; keep prefix fields limited and normalized.
- Bucketized ranges: With monthly buckets for dates, a 10-year window is 120 buckets. Most queries hit 1–3 buckets. Expect 1.2–1.5x false positive amplification you filter in the app. End-to-end overhead +10–30 ms p95 is typical.
- Encrypted text search via service: Indexes can be 2–5x the raw text, and ingestion adds +20–80 ms per write depending on batching. Reads add a network hop; budget +25–60 ms p95.
These are ballpark numbers we’ve seen across Postgres 14–16 and managed KMS in us-east regions. Your VPC topology and ORM behavior will swing them more than the crypto.
A Concrete Architecture That Ships
This is the pattern we recommend for startups and scale-ups building on Postgres (including Supabase/Postgres-as-a-service):
PII Vault + Blind Indexes + Optional Encrypted Search Service
- PII Vault microservice: A thin service that holds DEKs and salts in memory, talks to KMS, and exposes encrypt/decrypt/index functions over a tightly controlled RPC. Only your app and worker tiers can call it. It logs tokenization events, not plaintext.
- Application-side canonicalization: Lowercase, trim, and normalize. For Brazil, strip accents in names for search tokens, enforce E.164 on phone numbers, and validate CPF/CNPJ formats before encryption. The point of canonicalization is to avoid multiple ciphertexts for “the same” value.
- Postgres schema: For each sensitive field, store ciphertext (bytea) and one or more blind index columns (bytea or fixed-length bytea). Create B-tree or hash indexes on the blind index columns; never index the ciphertext itself.
- Triggers or app-layer writes: On insert/update, call the vault: encrypt plaintext and compute tokens; write ciphertext + tokens in one transaction. We prefer app-layer to keep the vault and salts out of the DB, but triggers are acceptable if they call out to a secure extension or FDW-like RPC.
- Query pattern: Convert user input to tokens in the app, query by tokens, fetch candidate rows, and decrypt only the subset you return. Never pull ciphertexts to the client.
- Encrypted search service (optional): For the 10–20% of fields that need fuzzy or ranked search, push documents to an encrypted search service coupled to your vault (e.g., CipherStash or a managed equivalent). Keep your DB as the source of truth and join on stable identifiers.
With this, your DB and cloud provider can’t read PII, but your app can still answer 95% of product queries at acceptable latency.
Build vs. Buy in 2026
You have three credible options:
- Roll your own with a vault and Postgres: Max control, minimal vendor lock, but you must own canonicalization, token generation, and rotation. Expect 4–6 weeks for a senior team to ship equality + prefix + bucketized ranges across 3–5 fields, then another 2–3 weeks to productionize key rotation, audit logs, and dashboards.
- Use a managed encrypted search layer (e.g., CipherStash): Faster time to value for tricky queries, consistent APIs, and built-in rotation. You’ll incur an extra hop and vendor cost, but you get a clearer leakage model and fewer footguns.
- Push some search to the client: For consumer apps, shifting name/address search to the device (with local indexes) can eliminate server-side leakage entirely. This is real on Android 17 and modern iOS, but UX and offline constraints apply.
Our heuristic: if you need more than equality + month-bucket ranges on 2–3 fields, buy the layer. If most queries are lookups by email/phone/ID, build the vault; you’ll recoup the investment in control and cost.
Common Footguns (And How to Avoid Them)
- Hashing instead of HMAC for blind indexes: SHA-256 without a key is a gift to attackers with rainbow tables. Use a keyed HMAC with per-tenant salts and a service-held pepper.
- Encrypting in the database: If your DB can decrypt, your cloud DBA can too. Keep keys and tokenization app-side or vault-side only.
- Skipping canonicalization: “John.Smith+promo@example.com” should equal “johnsmith@example.com” in your search semantics. Define, test, and freeze canonicalization per field.
- Unbounded prefix indexes: N-gram explosions happen fast. Cap prefix length you support (e.g., first 5 characters of local-part) and make it a product requirement.
- Rotating DEKs without versioning: Always write the key version used to encrypt into ciphertext metadata. Reads should try newest first, then older versions, then fail.
- No budget for false positives: Range buckets and n-grams require app-side filtering. Make it explicit in your latency SLOs and monitor p95/p99 separately from DB timings.
Migration Without a Weekend Outage
30 days: prove the pattern
- Pick two fields: email and birthdate. Ship deterministic encryption + equality blind index for email; month-bucket tokens for birthdate.
- Enable dual-write: plaintext columns remain authoritative for now; write ciphertext + tokens in parallel.
- Add read-through decryption for downstream systems that still need plaintext; log call sites.
60 days: flip reads and start rotation
- Switch queries to token-based lookups; decrypt only what you return.
- Backfill existing rows in batches (e.g., 10k/minute) during low-traffic windows. Measure index growth and p95 latency.
- Introduce DEK versioning; rotate 10% of tenants to v2; watch for regressions.
90 days: deprecate plaintext, extend coverage
- Gate plaintext access to a short allowlist and remove it from ORM models.
- Expand to phone and CPF/CNPJ with country-aware canonicalization.
- Decide on buy vs. build for any remaining fuzzy search. If buying, integrate the encrypted search service now and retire ad hoc indexes.
Compliance and Residency: What Regulators Actually Care About
HIPAA, GLBA, and Brazil’s LGPD won’t prescribe algorithms. They will ask if unauthorized parties (including your cloud) can read PII at rest. Field-level encryption with app-held keys is a strong answer. If you operate in the US and Brazil, keep DEKs resident where regulators expect them (often the same region as the data store) and document flows. When discovery hits, you want to show that production DB snapshots are cryptographically inert without your app tier.
What to Tell Your Product Team
Encrypted search isn’t free. Make three constraints explicit upfront:
- Exact match semantics are fast; fuzzy is not: Prefixes limited to N characters, names normalized and accent-insensitive at tokenization time.
- Ranges are bucketized: Birthdate searches are by month or quarter, not arbitrary day-of-week filters.
- Cost shows up in storage and a small latency tax: Plan for 1.3–2.0x index growth on the encrypted fields and +10–30 ms p95. This is the price of not handing your cloud the keys.
Where Supabase + CipherStash Fit
If you’re already on Supabase or managed Postgres, an encrypted search integration (like the recent Supabase + CipherStash work) can remove a lot of yak shaving: client libraries for tokenization, sane defaults for equality/prefix/range, and managed rotation. Your leakage model won’t be zero — order and access patterns still leak — but you’ll have a product team maintaining those models and a migration path that doesn’t require a crypto PhD. Evaluate on three axes: query coverage (do your real queries fit?), latency SLOs with your data shape, and key control (can you bring your own KMS?).
The Brazilian Angle: Don’t Let Localization Break Your Crypto
For US–Brazil stacks, normalization matters more than usual:
- Names: Strip diacritics for search tokens but preserve them in ciphertext; build locale-aware comparators.
- Phones: Enforce E.164 with DDD handling; WhatsApp-driven data often arrives malformed. Normalize or you’ll miss matches.
- CPF/CNPJ: Validate and normalize (strip punctuation) before tokenization. Consider format-preserving display only at render-time after decryption.
Get normalization wrong and you’ll multiply ciphertexts for the same user, exploding index sizes and torpedoing match rates.
When Not to Do This
If your core value proposition is fuzzy search over inherently sensitive text (e.g., a people search engine), you either need a client-side heavy architecture or to accept that your ops team can read data. Half measures will give you cost without real privacy. For everyone else — mainstream SaaS handling user records, transactions, and support workflows — the techniques above are enough to move you from “encryption theater” to substantive control.
Bottom Line
Searchable encryption stopped being a research project. Equality and practical range queries on encrypted fields are well within reach with deterministic encryption, blind indexes, and disciplined key management. Add an encrypted search service for the 10–20% of queries that need it, and your app can finally answer support, fraud, and billing queries without giving your cloud provider or your DBA plaintext. That’s a material risk reduction you can explain to your board — and a posture your customers will notice the next time a competitor leaks a snapshot.
Key Takeaways
- Decide what you’re protecting against first: cloud/DB insiders and snapshot leaks are realistic; fully compromised app tiers are not fixable with crypto alone.
- Use deterministic encryption or blind indexes for equality; bucketize ranges; push fuzzy search to a client or encrypted search service.
- Keep keys out of the database: per-tenant DEKs, KMS-backed KEK, short in-memory TTLs, and explicit key versioning.
- Budget 1.3–2.0x index storage on encrypted fields and +10–30 ms p95 latency for most queries.
- Normalize aggressively (emails, phones, CPF/CNPJ, diacritics) before tokenization or you’ll miss matches and bloat indexes.
- Roll your own vault if you only need equality + coarse ranges; buy an encrypted search layer if you need fuzzy or ranked text search.
- Migrate in 90 days with dual-writes, read flips, and gradual key rotation; don’t attempt a single weekend cutover.