vaultaris /docs

Security

Security architecture, cryptographic choices, and hardening recommendations for Vaultaris.

Cryptography

PurposeAlgorithm
Password hashingArgon2id (memory-hard, OWASP-recommended parameters)
OAuth token signingRotating ed25519 / ECDSA P-256 keys (JWK set)
Internal token signing (MFA, setup)HMAC-SHA256 (HS256)
Sensitive data at rest (TOTP secrets)AES-256-GCM via vaultaris-crypto crate
Token generationCSPRNG via OS (rand + getrandom)
PKCE challengeSHA-256 + base64url
DPoP proof verificationed25519 / P-256 ECDSA — RFC 9449
WebAuthn credential signaturesES256 (ECDSA P-256 + SHA-256), RS256 fallback
WebAuthn challenge bindingSHA-256 of clientDataJSON
Billing shadow service tokenConstant-time comparison (subtle crate)

DPoP — Sender-Constrained Tokens

DPoP (RFC 9449) prevents token replay attacks by cryptographically binding access and refresh tokens to the client's key pair. A stolen token cannot be used by an attacker who doesn't possess the private key.

Vaultaris verifies:

  • The DPoP header JWT is signed by the key in the jwk claim
  • The htm/htu claims match the actual request method and URL
  • The iat claim is within the acceptable clock skew window
  • The jti is unique (replay prevention)
  • The token's cnf.jkt thumbprint matches the proof's public key

WebAuthn / Passkeys

W3C WebAuthn Level 2 server-side verification (no external library). Covers: CBOR attestation object parsing, clientDataJSON verification, authenticatorData parsing, COSE public key extraction, sign counter validation (clone detection), challenge binding.

rpId and expected origin are derived from EXTERNAL_URL. Changing EXTERNAL_URL invalidates all stored passkey credentials.

Security properties:

  • Challenges expire after 5 minutes, single-use
  • Sign counter regression → 400 response (indicates potentially cloned authenticator)
  • Unique (tenant_id, credential_id_base64) constraint per tenant

Passwords

Argon2id via the argon2 crate. Constant-time comparison. Password reset tokens: 32 bytes → 64 hex chars, 24-hour TTL, single-use. Password policy enforcement: minimum length, complexity, history, max age, and lockout threshold are configurable per tenant.

Sessions

Sessions stored in PostgreSQL — not stateless JWTs. Instantly revocable server-side. Tied to IP and user-agent. Global sessions validate the requesting domain against a per-session allowlist with wildcard matching.

Device Registry

Every login registers or updates a device record derived from the User-Agent. New devices trigger an email alert to the user and fire the NEW_DEVICE_REGISTERED plugin hook, enabling security notifications and anomaly detection.

Admins and users can trust or revoke individual devices from the dashboard.

Rate Limiting

IP-level, evaluated before business logic. Default: 120 requests / 60-second window. Distributed via Redis sliding-window algorithm (Lua script). Falls back to in-memory fixed-window when Redis is unavailable. Returns 429 Too Many Requests with Retry-After.

Tenant Isolation

All queries are scoped by tenant_id at the middleware layer. AuthenticatedUser extractor enforces the tenant_id claim in the Bearer token matches the route's {tenant_id} path parameter. Cross-tenant access is impossible except from the master tenant.

Email Enumeration Prevention

Password reset always returns HTTP 200 regardless of whether the email exists in the system. The response body is identical in both cases.

Audit Trail

Every auth event, token issuance, role change, freeze/unfreeze action, and admin operation produces an append-only audit record with actor, action, target resource, timestamp, IP, and user-agent.

OWASP Top 10

RiskMitigation
A01 Broken Access ControlTenant-scoped DB queries + AuthenticatedUser middleware
A02 Cryptographic FailuresArgon2id, AES-GCM, CSPRNG, DPoP sender-binding
A03 InjectionSQLx parameterized queries only — no string interpolation
A04 Insecure DesignOAuth 2.0 + PKCE; email enumeration prevention; DPoP
A05 Security MisconfigurationSane defaults; production checklist in docs
A06 Vulnerable ComponentsPure Rust audited crates; cargo audit in CI
A07 Auth FailuresArgon2 + token expiry + session revocation + lockout
A08 Software IntegrityDeterministic builds; release binary SHA-256 checksums
A09 Logging & MonitoringStructured tracing + append-only audit log
A10 SSRFNo user-controlled URL fetching in core paths

Secrets Management

Never commit secrets to git or embed them in Docker images or Kubernetes ConfigMaps. Use a dedicated secrets manager:

  • AWS Secrets Manager / Parameter Store
  • HashiCorp Vault
  • 1Password Secrets Automation
  • Doppler

Kubernetes: inject secrets as environment variables from kind: Secret objects, not ConfigMaps.

TLS

Terminate TLS at the edge (nginx, Caddy, cloud load balancer). Vaultaris listens on plain HTTP. Never expose port 8080 to the internet directly. Minimum TLS 1.2; prefer TLS 1.3.

Caddy example (auto Let's Encrypt):

auth.example.com {
  reverse_proxy localhost:8080
}

License Enforcement & Freeze Security

The freeze system ensures that Vaultaris cannot be used to exceed purchased resource limits. Freeze operations are audited, and the FreezeReason enum distinguishes automatic (license-driven) freezes from manual admin freezes. Manual freezes are never automatically reversed on upgrade — they require explicit admin action.

Responsible Disclosure

GitHub Security Advisories or security@vaultaris.net. 90-day coordinated disclosure policy.