Security Overview

Security Overview

Security Overview

Formic employs a defense-in-depth security model with multiple independent layers of protection. Every form submission passes through a pipeline of security checks before it is stored and delivered.

Security Pipeline

Incoming Request


┌─────────────────┐
│  TLS/HTTPS      │  ← Terminated at Caddy reverse proxy
│  (Transport)    │
└─────────────────┘


┌─────────────────┐
│  API Key Auth   │  ← X-API-Key header verified via bcrypt
│  (Identity)     │
└─────────────────┘


┌─────────────────┐
│  Turnstile CAPT │  ← Cloudflare Turnstile anti-bot check
│  (Anti-Bot)     │
└─────────────────┘


┌─────────────────┐
│  Origin Check   │  ← Per-form allowed origins allow-list
│  (CORS Policy)  │
└─────────────────┘


┌─────────────────┐
│  Payload Limit  │  ← 2MB maximum request body
│  (Size Control) │
└─────────────────┘


┌─────────────────┐
│  JSON Schema    │  ← Structural validation against form schema
│  (Validation)   │
└─────────────────┘


┌─────────────────┐
│  XSS Sanitize   │  ← HTML entity escape, Unicode normalize
│  (Data Clean)   │
└─────────────────┘


┌─────────────────┐
│  Rate Limit     │  ← Per-form + global rate limits (Redis)
│  (Abuse Control)│
└─────────────────┘


   Delivered

Feature Summary

FeatureDescriptionDetails
AuthenticationAPI key via X-API-Key headerbcrypt-hashed keys, anti-enumeration (401 for invalid key AND non-existent forms)
Anti-BotCloudflare Turnstile validationRequired on all submissions; 502 if Turnstile API unreachable
Rate LimitingPer-form + global limitsRedis-based, fail-closed, configurable limits, Retry-After header
Origin ValidationPer-form allow-list403 Forbidden for disallowed origins
Payload LimitsRequest body size enforcement2MB default, configurable via MAX_PAYLOAD_SIZE_BYTES
Data SanitizationXSS preventionHTML entity escaping, Unicode NFKC normalization, string truncation
Schema ValidationJSON Schema (Draft 2020-12)Runs before sanitization; format validation included
Secret MaskingCredentials never loggedAPI keys, connection strings masked in log output

Transport Security

All communication with the Formic API is encrypted in transit:

  • Client → API: HTTPS terminated at the Caddy reverse proxy layer
  • API → Database: TLS for PostgreSQL connections
  • API → Email: TLS for SMTP (Lettre with tokio1-rustls-tls)
  • API → Cloudflare: HTTPS for Turnstile verification