Skip to main content
This policy governs when we encrypt, with what, and how we manage keys. Encryption is the implementation detail; this is the policy that implementation must conform to.
Owner: CTO (implementation) · Approved by: Security Officer · Policy version: 0.9 (draft) · Reviewed: Annually

Requirement by data classification

Per the Data classification policy:
ClassificationAt restIn transitAdditional
Restricted — PHIAES-256 via CMEK + column-level AR encryptionTLS 1.2+PHI scrubbing before LLM/observability egress
ConfidentialAES-256 via CMEKTLS 1.2+Secrets only in Secret Manager
InternalEncryption at rest inherited from Google Workspace/tool providerTLS to the tool
PublicNo requirementTLS (HTTPS) for integrity

Approved algorithms

Symmetric

AES-256-GCM for authenticated encryption (the default for Active Record Encryption and GCP CMEK).

Asymmetric

RSA-2048+ or ECDSA P-256+ for signing. Ed25519 preferred for new signing needs.

Hashing

SHA-256 or SHA-512 for integrity. Argon2id or bcrypt for password storage (Devise uses bcrypt by default).

Transport

TLS 1.2 minimum; TLS 1.3 preferred. SSLv3, TLS 1.0, TLS 1.1 disabled at the load balancer.

Prohibited

  • MD5, SHA-1 — not allowed for integrity or signing (still allowed for non-security checksums like ETags).
  • DES, 3DES, RC4 — not allowed anywhere.
  • Weak RSA (< 2048-bit), DSA, DH with weak parameters.
  • Self-signed certificates on any production endpoint.
  • Home-rolled crypto — use vetted libraries only.

Key management

Key types

KeyPurposeStorageRotation
Cloud SQL CMEKAt-rest encryption for PostgresGCP KMSAutomatic, every 90 days
GCS CMEKAt-rest encryption for GCS bucketsGCP KMSAutomatic, every 90 days
Memorystore CMEKAt-rest encryption for RedisGCP KMSAutomatic, every 90 days
Terraform state CMEKAt-rest encryption for TF state bucketGCP KMSAutomatic, every 90 days
Active Record primary keyColumn-level encryption of PHIGCP Secret ManagerManual — requires re-encryption (see below)
Active Record deterministic keyColumn-level encryption of queryable PHI (e.g. names)GCP Secret ManagerManual — requires re-encryption
Active Record key-derivation saltInput to AR encryption KDFGCP Secret ManagerManual
JWT signing key (DEVISE_JWT_SECRET_KEY)Session JWTsGCP Secret ManagerManual (dual-secret support for zero-downtime)
TLS certificatesHTTPS terminationManaged by GCP Load BalancerAuto-renewed
OAuth client secretsThird-party OAuth integrationsGCP Secret ManagerPer third-party rotation + annual

Generation

  • Keys generated via approved sources: GCP KMS (for CMEK), SecureRandom.hex(64) for application secrets, GCP for service account Workload Identity (no key material, token exchange).
  • No keys derived from user input (passwords, email).
  • No keys embedded in source, env files, or CI variables.

Storage

  • GCP Secret Manager for all application-layer secrets.
  • GCP KMS for CMEK and anything that uses Google-managed key material.
  • Memorystore for session tokens / OTP tokens (short-TTL, not cryptographic keys themselves).
  • Never in source control, .env files, workstations, or anywhere outside those systems.

Access

  • Runtime service account (Cloud Run, worker VM) has secretmanager.secretAccessor only on the secrets it needs.
  • No developer has direct read access to production secrets.
  • Secret access events are audit-logged in GCP Cloud Audit Logs.
  • Break-glass read is possible under the access control policy.

Rotation

Per the SECRET_ROTATION.md runbook and summarized on this page.
KeyCadenceMechanism
CMEK keys90 daysAutomatic (GCP KMS)
JWT signing key90 daysDual-secret rotation (zero downtime)
AR encryption keysOnly on compromiseRe-encryption required
Database password180 daysCoordinated redeploy
API keys (third-party)365 days or per providerManual
OAuth client secrets365 daysManual + provider coordination

Compromise response

If any key is suspected of compromise:
  1. Immediate: rotate the affected key (emergency procedure — accept brief session disruption if needed).
  2. Within 1 hour: audit log review for anomalous use of the compromised key.
  3. Within 24 hours: determine blast radius; notify affected customers if PHI exposure possible.
  4. Within 7 days: full post-mortem including root cause and corrective actions.
See Incident response.

Certificate management

  • All external endpoints use TLS certificates managed by GCP Load Balancer.
  • Automatic renewal; no manual cert installation in production.
  • Staging and prod have independent certificate pools.
  • Pinned to Google’s trusted CA hierarchy.

Cryptographic use in the application

Sessions are JWTs signed with HS256 using a 64-byte secret. HttpOnly + Secure + SameSite=Lax cookies. See Authentication.
Active Record Encryption with AES-256-GCM. Deterministic mode for queryable columns (names, emails, member IDs); non-deterministic for free-text and date columns.
Devise default bcrypt with cost factor 12 (dev) / 13 (prod) — upgradeable without re-hashing.
RFC 6238 with 160-bit secrets; encrypted in the database via AR encryption.
Standard WebAuthn using the user’s platform or roaming authenticator; no server-side private key material.
Signed tokens tied to the session; verified on every state-changing request.
HMAC-SHA256 with provider-specific shared secrets.

Hardcoded-secret prevention

  • gitleaks runs on every commit (CI + pre-push).
  • Brakeman flags suspicious crypto patterns in Ruby.
  • 1Password for any human-held secret; no secrets in personal notes or chat.
  • Pair-programming review of any crypto-adjacent code.

Export controls

Denialbase uses standard symmetric and asymmetric algorithms available from the United States under ECCN 5D002 with self-classification and (where applicable) BIS filings. Distributions are via GCP’s hosted infrastructure; we don’t export standalone cryptographic code.

Exceptions

Any deviation from this policy requires written approval from the Security Officer with a documented rationale, risk acceptance, and expiry. Tracked in the Risk register.