Security Hardening
Sovrium designs out the recurring failure modes of insecure apps — exposed surfaces, missing auth, broken object-level authorization, enumerable endpoints — at the platform layer, so every app inherits the defenses with no per-app configuration. Every HTTP response carries a hardened header set, CSRF is enforced in production, sensitive endpoints are rate-limited, and unauthorized access returns 404 rather than 403.
These guarantees are applied in code (not only at the reverse-proxy ingress), so they hold even for a self-hosted deploy with no proxy attached.
HTTP Security Headers
Every response — page, API, static asset, or 404 — carries a hardened header set, applied via Hono's secureHeaders middleware registered as the first * middleware so it runs before route matching and covers error responses.
| Header | Value / behavior |
|---|---|
Strict-Transport-Security |
max-age=31536000; includeSubDomains (1-year HSTS, covers subdomains). |
X-Content-Type-Options |
nosniff (disables MIME sniffing). |
X-Frame-Options |
SAMEORIGIN / DENY (anti-clickjacking). |
Referrer-Policy |
strict-origin-when-cross-origin. |
Cross-Origin-Opener-Policy |
same-origin. |
Cross-Origin-Resource-Policy |
same-origin. |
Permissions-Policy |
Denies camera, microphone, geolocation, payment, usb, accelerometer, … |
Content-Security-Policy-Report-Only |
default-src 'self', frame-ancestors 'none', object-src 'none', base-uri 'self', form-action 'self', … |
Content-Security-Policy (enforced) |
Absent — report-only first; the enforced, nonce-based CSP is a Phase 2 promotion. |
CSP ships report-only first. It surfaces violations without blocking while the SSR layer still emits inline <script>/<style>. The enforced header is deliberately absent for now, and a spec locks that absence in so a premature flip fails CI. The in-code directives mirror the Caddy ingress backstop so both layers agree.
HSTS is emitted unconditionally even over plain HTTP — browsers ignore it on an http:// connection, and a TLS-terminating proxy forwards exactly the value the server produces.
CSRF & Cross-Origin Enforcement
Sovrium relies on Better Auth's built-in origin-check middleware. A state-changing, cookie-bearing request whose Origin is forged or stripped is rejected — a cross-site forgery cannot ride a victim's session cookie.
| Environment | disableCSRFCheck |
Behavior |
|---|---|---|
NODE_ENV=development |
true |
Origin check bypassed — forged Origin accepted (local cross-port DX). |
NODE_ENV=production |
false |
Origin check active — forged / missing Origin rejected 403. |
trustedOriginsis scoped to the app's ownBASE_URLorigin — never'*'. A wildcard is an open-redirect / origin-validation bypass; scoping it also makes Better Auth reject an external-originredirectToon password reset.useSecureCookiesis enabled in production. Both the CSRF gate and secure cookies silently degrade ifNODE_ENVis unset, so a missing-NODE_ENVrisk is surfaced loud in the production startup banner (and suppressed in dev to keep output clean).
Set NODE_ENV=production in production. CSRF enforcement and secure cookies both key off it. When unset, the server warns loudly in the startup banner — heed it before going live.
Rate Limiting
Sovrium replaces Better Auth's native rate limiting (known upstream bugs) with custom Hono middleware on the sensitive endpoints.
| Endpoint | Limit | Window |
|---|---|---|
POST /api/auth/sign-in/email |
5 attempts | 60 s |
POST /api/auth/sign-up/email |
5 attempts | 60 s |
POST /api/auth/request-password-reset |
3 attempts | 60 s |
POST /api/auth/admin/* |
General per-IP rate limit | — |
Exceeding a limit returns 429 Too Many Requests. Admin routes additionally run an auth-check middleware that returns 401 before parameter validation, so an unauthenticated caller never learns the shape of a protected endpoint.
Anti-Enumeration: 404, Not 403
Unauthorized access to a protected resource returns 404 Not Found, never 403 Forbidden. A 403 confirms the resource exists; a 404 makes the resource's existence — and the id space — unobservable. This applies uniformly across record reads, the admin dashboard, and the per-table force-delete endpoint (which returns 404 when force-delete is not allowed for that table).
Object-level authorization is enforced before any sensitive logic runs: a request is authenticated, then checked for access to the specific object, and on failure returns 404. Combined with unguessable ids, this defeats enumeration even though the 404 itself is observable.
Defense-in-Depth Summary
| Concern | Mechanism |
|---|---|
| Transport security | HSTS (max-age=31536000; includeSubDomains). |
| MIME / clickjacking | nosniff, X-Frame-Options. |
| Cross-origin isolation | COOP + CORP same-origin, report-only CSP. |
| CSRF | Better Auth origin check (production), scoped trustedOrigins. |
| Brute-force | Per-endpoint rate limiting (429). |
| Enumeration | 404-not-403, unguessable ids, auth-check before validation. |
| Authorization | RBAC + field-level permissions, object-level checks. |
Related Pages
- Authentication Overview — auth strategies and the
authblock. - Sessions — session lifetime, revocation, secure cookies.
- Roles & RBAC — role model and field-level permissions.
- Table Permissions — per-table and per-field access control.
- Admin Dashboard — the 404-on-unauthorized read surface.
- GDPR & Privacy — data export and erasure guarantees.
- Environment Variables —
NODE_ENV,BASE_URL, and related settings.