Appearance
Encryption Policy
Status: Active. Effective 2026-05-11. Owner: Taha Abbasi. Reviewed: Annually, or whenever a new data store / new vendor / new region is added.
Purpose
Document the cryptographic posture protecting AskFlorence data at rest and in transit, and the key-management practices around AWS KMS, MongoDB Atlas, and third-party encryption surfaces. Required artifact for HIPAA §164.312(a)(2)(iv) (encryption + decryption), §164.312(e)(1) (transmission security); SOC 2 CC6.7 (transmission encryption); CMS EDE Phase 3 / MARS-E 2.2 SC-12, SC-13, SC-28; NIST 800-53 R4 Moderate SC-8, SC-13, SC-28.
Scope
Applies to all AskFlorence systems, applications, and data stores — production, staging, and management AWS accounts; both Atlas projects; all S3 buckets; all Secrets Manager entries; all transit between any of the above; all employee endpoints with persistent access to production data.
Encryption in transit
| Path | Floor | Mechanism | Evidence |
|---|---|---|---|
Public internet → CloudFront edge (apex askflorence.health) | TLS 1.2+ | ACM-managed cert; HTTPS-only redirect | CloudFront distribution config; ACM cert ARN in infra/envs/prod/acm.tf |
| CloudFront → ALB | TLS 1.2+ | ACM cert; ALB HTTPS listener only | infra/envs/prod/alb.tf |
| ALB → ECS Fargate task | TLS 1.2+ (internal) | Target-group HTTPS | infra/envs/prod/alb.tf |
| ECS Fargate task → MongoDB Atlas (prod cluster + cross-cluster reference) | TLS 1.2+ (Atlas-side floor) | Atlas-enforced; mongodb+srv connection strings resolve to TLS-only endpoints | Atlas project config; ADR 0004 for cross-cluster path |
| ECS Fargate task → AWS Secrets Manager | TLS 1.2+ | AWS-internal endpoint over private network | infra/envs/prod/network.tf (VPC endpoint configured) |
| ECS Fargate task → AWS SES | TLS 1.2+ | AWS-internal endpoint over private network | infra/envs/prod/network.tf + ses.tf |
| Cross-cluster Atlas reference reads (prod VPC → staging Atlas) | TLS 1.2+ at app layer + AWS-backbone-only at network layer | AWS PrivateLink endpoint vpce-0c81aea11e29bb928 targeting Atlas endpoint service com.amazonaws.vpce.us-east-1.vpce-svc-0d8138ea0f6542afa | ADR 0004; infra/envs/prod/atlas-staging-privatelink.tf |
| GitHub Actions runner → AWS (deploy / Terraform) | TLS 1.2+ | OIDC federation, short-lived STS tokens; no long-lived IAM keys | infra/envs/*/github-oidc.tf |
| Developer laptop → Atlas (admin operations) | TLS 1.2+ + IP allowlist + Atlas user MFA | Standard Atlas connection | access-control-policy.md |
Standard: TLS 1.2 minimum across every channel. TLS 1.3 preferred where supported. Plain HTTP / unencrypted protocols (FTP, telnet, plaintext SMTP) are forbidden — none exist in our infrastructure today; this policy holds them out for the future.
Encryption at rest
| Data store | Mechanism | Key | Evidence |
|---|---|---|---|
MongoDB Atlas — prod cluster (askflorence-prod-01, M10 HIPAA) | AES-256 disk encryption (Atlas default at HIPAA tier) | Atlas-managed key | Atlas project HIPAA tier enablement; see vendor register Atlas row |
MongoDB Atlas — staging cluster (askflorence-staging, M30) | AES-256 disk encryption (Atlas default) | Atlas-managed key | Atlas project config |
AWS S3 — askflorence-tfstate-* (Terraform state buckets, one per account) | SSE-KMS | Project CMK aws_kms_key.tfstate per account | infra/envs/{mgmt,prod,staging,log-archive}/ Terraform |
AWS S3 — askflorence-data (mgmt account, shared data + script outputs) | SSE-KMS | Project CMK aws_kms_key.askflorence_data (mgmt account) | infra/envs/management/s3-askflorence-data.tf |
AWS S3 — org_cloudtrail_logs (log-archive, CloudTrail org-trail) | SSE-KMS | Log-archive CMK aws_kms_key.org_logs | Phase 2 Terraform (CLI-managed pending Phase 3b import) |
AWS S3 — org_config (log-archive, AWS Config snapshots) | SSE-KMS | Log-archive CMK | Phase 2 Terraform (CLI-managed pending Phase 3b import) |
| AWS Secrets Manager — all prod + staging secrets | SSE-KMS at the secret level | Project CMK | infra/envs/{prod,staging}/secrets.tf |
| AWS EBS volumes (ECS task ephemeral storage) | AES-256 default | AWS-managed key (acceptable for ephemeral) | ECS Fargate default behavior |
| CloudWatch Logs (ECS task logs, GuardDuty findings, etc.) | AWS-managed encryption | AWS-managed key | Service default |
DynamoDB (Terraform state locks askflorence-tfstate-locks) | SSE-KMS default | AWS-managed key | infra/envs/*/providers.tf |
Standard: every data store enforces encryption-at-rest by default. No bucket may be created without SSE; no Atlas cluster below the HIPAA tier may hold PHI; no Secrets Manager entry may be created without project CMK encryption. CI enforcement: infra/modules/secrets-admin/ writes a per-secret KMS key reference; ad-hoc aws secretsmanager create-secret calls without --kms-key-id are caught at PR time (the Terraform module is the only path).
Field-level encryption (application layer)
Current state: not implemented. No collections currently hold PHI requiring CSFLE-grade per-field encryption.
When this changes: the consumers and enrollments collections (Phase 5+, target after 2026-06-15 platform v1 + post-funding) will hold SSN, DOB, plan-enrollment records, and other PHI. Before these collections are created and exposed to the application path:
- CSFLE (MongoDB Client-Side Field Level Encryption) with AWS KMS as the master-key provider.
- Per-field DEKs (Data Encryption Keys) — one per sensitive field, encrypted with a KMS CMK.
- Per-CMK isolation — SSN field encrypted with one CMK; DOB / name / address encrypted with a separate CMK. Compromise of one CMK does not yield the other field.
- Key access — KMS CMK access restricted to the specific IAM role that backs the application's CSFLE client. Audit-logged via CloudTrail.
- Pre-launch verification probe — exercise the encryption + decryption path on synthetic data, then verify the on-disk Atlas representation is binary blobs (not plaintext).
Decision artifact: an ADR will be filed at the time of CSFLE rollout capturing the field-by-field key plan + KMS CMK ARN + IAM binding. Until then, no PHI lands on Atlas.
Key management (AWS KMS)
| Key | Account | Purpose | Rotation |
|---|---|---|---|
aws_kms_key.tfstate | each env account | Terraform state bucket + DynamoDB lock encryption | Annual auto-rotation enabled on all CMKs |
aws_kms_key.askflorence_data | management | Shared data bucket encryption | Annual auto-rotation |
aws_kms_key.org_logs | log-archive | CloudTrail + Config log encryption | Annual auto-rotation |
| Secrets Manager per-secret keys | prod + staging | Per-secret SSE-KMS | Annual auto-rotation; secrets value-rotation cadence in access-control-policy |
Standard:
- All AWS KMS CMKs have automatic-rotation enabled (annual). Verification:
aws kms list-aliases+aws kms describe-keyper CMK; expectKeyRotationEnabled: true. - CMK access is restricted via IAM key policy + IAM principal policy to the specific roles that need it. No
Resource: "*"grants on KMS keys. - CMK deletion is window-protected (30-day pending-delete window standard); irreversible-delete requires explicit operator approval + ADR.
- Cross-account CMK usage (e.g., log-archive CMK consumed by prod CloudTrail trail) is permitted only when documented in Terraform with explicit principal ARNs.
- No customer-managed external keys (BYOK) today; if a vendor or customer requires HSM-backed BYOK we evaluate AWS CloudHSM.
TLS certificate management
| Surface | Authority | Renewal |
|---|---|---|
askflorence.health apex + www (CloudFront) | AWS ACM | Auto-renewed by ACM (60 days before expiry) |
stage.askflorence.health (staging ALB) | AWS ACM | Auto-renewed by ACM |
| Atlas cluster endpoints | Atlas-managed | Atlas-managed; we don't touch |
| ALB internal listeners | AWS ACM | Auto-renewed by ACM |
Standard: AWS ACM is the only TLS cert authority. No self-signed certs in production. Cert ARN drift is caught by Terraform terraform plan runs in CI.
Cryptographic algorithms
Approved:
- Symmetric: AES-256 (GCM mode preferred; CBC acceptable where required by upstream library)
- Asymmetric: RSA-2048+ or ECC-P-256+
- Hashing: SHA-256+ (SHA-1 deprecated; MD5 forbidden)
- Password hashing (future, agent + admin auth): argon2id (NIST 800-63B compliant)
- Key derivation: PBKDF2 / scrypt / argon2id depending on use case
Forbidden:
- DES, 3DES, RC4, MD5, SHA-1 for any new code path
- Hard-coded encryption keys or IVs in source code (CI secret-scanning catches this)
- Custom cryptographic primitives (use vetted libraries only —
cryptoin Node.js,cryptographyin Python, KMS for envelope encryption)
Endpoint encryption (workforce devices)
| Device class | Encryption | Verification |
|---|---|---|
| Founder + ops laptops (macOS) | FileVault 2 (AES-XTS) enabled | Verified at quarterly access review |
| Mobile devices with email access | Google Workspace Mobile Device Management — encryption-required policy | Cloud Identity device-compliance enforcement |
| Removable media | Not used for production data; if ever used, must use platform-native encryption | Quarterly access review confirms no removable media in inventory |
Backup encryption
| System | Backup type | Encryption |
|---|---|---|
| MongoDB Atlas (both clusters) | Continuous snapshots (Atlas-managed) | Encrypted at rest (same CMK / Atlas-managed key as live data) |
| AWS S3 buckets | Versioning enabled on all stateful buckets | Versions inherit object SSE-KMS |
| Terraform state | S3 versioning on tfstate buckets | SSE-KMS |
| Code (GitHub) | GitHub-managed | GitHub at-rest encryption (no PHI / secrets in repo by .gitignore + secret-scanning) |
Decommissioning + media disposal
We do not run on self-managed hardware. Decommissioning is the responsibility of AWS + MongoDB Atlas under their respective BAAs:
- AWS — NIST SP 800-88 media sanitization in scope of FedRAMP Moderate ATO; referenced in AWS Organizations BAA signed 2026-04-18
- MongoDB Atlas — referenced in Atlas BAA (M10 HIPAA tier coverage), to be confirmed by signed PDF per #57
If we ever provision self-managed hardware (e.g., founder laptop replacement, on-prem appliance), the disposal procedure goes through the offboard team member runbook for the laptop case + a one-off device-disposal runbook for any other case.
FedRAMP migration
Atlas commercial → Atlas for Government migration is planned at the CMS EDE Phase 3 cutover (~Feb 2027 submission). The migration:
- Same architectural posture transfers unchanged — narrow Mongo roles, append-only audit log, PrivateLink (Atlas Gov supports the same endpoint pattern, see ADR 0004 revisit-trigger #3)
- Same encryption posture transfers unchanged — Atlas-Gov enforces the same AES-256-at-rest + TLS-in-transit floor at the FedRAMP-Moderate-authorized boundary
- Migration window: rehearsed on a staging Atlas Gov sandbox before the prod cutover
Exceptions
None today. Any future exception (e.g., a vendor that requires a weaker algorithm, a system that cannot enforce encryption-at-rest) must be:
- Documented in an ADR with explicit risk acceptance signed off by Taha + Asad
- Time-bounded with a remediation plan
- Re-reviewed at the next quarterly access review
Reference
- SOC 2 Control Mapping — CC6.7 (transmission encryption) row
- HIPAA Control Mapping — §164.312(a)(2)(iv), §164.312(e)(1) rows
- CMS EDE Appendix A Mapping — §4 (Data Transmission Encryption), §5 (Data-at-Rest Encryption) rows
- ADR 0004 — Cross-cluster Atlas PrivateLink
- Access Control Policy — credential / key rotation cadence
- Data Classification Policy — what data requires which encryption tier
- Vendor / Subprocessor Register — KMS / encryption coverage per vendor BAA