Security Hardening

📦v1.0.0📅2026-04-28🔄Updated 2026-04-28👤Admin Team
administrationoperationssecurity

Security Hardening

This page covers the production security measures built into Message Center and the additional configuration required for a hardened deployment.


Content Security Policy (CSP)

Message Center enforces a strict Content Security Policy in production. CSP headers are set by the Next.js middleware and block inline scripts, untrusted origins, and resource loading from unexpected domains.

The CSP is configured in src/middleware.ts. The default policy:

  • default-src 'self' — blocks all external resources by default
  • script-src 'self' — no inline scripts, no eval()
  • style-src 'self' 'unsafe-inline' — inline styles permitted (required by Tailwind/shadcn)
  • frame-src — expanded to include GRAFANA_PUBLIC_URL when set (for the monitoring iframe)
  • connect-src 'self' — API calls restricted to same origin

Verifying CSP in production

make test-csp   # runs the CSP guard test suite

The CSP guard tests (tests/csp*.test.*) verify that the policy headers are present on all routes and that no scripts are loaded from unexpected origins.

If you add an external embed (e.g., a new iframe), update src/middleware.ts to extend the relevant frame-src or script-src directive.


RBAC Lockdown

Message Center uses role-based access control with four roles: author, moderator, admin, super_admin. In addition, the is_moderator flag on an author membership grants elevated permissions within one workspace.

Hardening recommendations:

  1. Minimize super_admin accounts. Super admins see all workspaces and can delete campaigns in any workspace. Assign super_admin only to system operators, not workspace owners or content managers.

  2. Assign authors rather than admins. Authors can create campaigns but cannot manage members or sender names. Assign the admin role only where workspace management is needed.

  3. Use is_moderator flag instead of promoting authors to full admin when moderator permissions are needed within one workspace.

  4. Audit role assignments. The audit log records all membership changes (member.invited, member.role_changed). Review via the Audit tab on the Settings page or directly in MongoDB.

See the full permission matrix at Roles & Permissions.


Secret Rotation

SecretRotation cadenceHow to rotate
NEXTAUTH_SECRETQuarterlyGenerate new value; existing sessions invalidated on next request (users must re-login)
MONGODB_URI (credentials)QuarterlyUpdate MongoDB user password; update Secret in k8s; rolling restart
BFF_PROXY_EMAIL / BFF_PROXY_PASSWORDQuarterlyUpdate service account in Proxy; update k8s Secret; the JWT cache refreshes automatically within 60 s
CORE_ADMIN_API_KEYQuarterlyRotate in Core config; update k8s Secret; rolling restart
mTLS certificatesBefore expiry (check NotAfter)Replace files; see [[v1.0.0/en/public/04-administration/02-configuration/04-mtls-certificates]] for hot-reload procedure — no restart needed

Rotating NEXTAUTH_SECRET

All active user sessions are signed with the current NEXTAUTH_SECRET. Rotating it immediately invalidates all sessions — all users will be logged out. Coordinate with users or schedule rotation during a maintenance window.

# Generate a new secret
openssl rand -base64 32

# Update Kubernetes Secret
kubectl patch secret message-center-secrets \
  -p '{"stringData":{"NEXTAUTH_SECRET":"<new-value>"}}'

# Trigger rolling restart to pick up the new secret
kubectl rollout restart deployment/core-admin

Network Security

Kubernetes NetworkPolicy

Restrict pod communication at the network level (see Kubernetes Deployment for the full NetworkPolicy example). The policy should:

  • Allow inbound only on port 3000 (web UI)
  • Allow outbound only to MongoDB (27017), Core API (8080/8092), and Proxy (8088/8089)
  • Block all other outbound — prevents data exfiltration via server-side request forgery

TLS everywhere

  • The web UI should be served behind TLS termination (nginx Ingress with a valid certificate, or a load balancer).
  • Core and Proxy connections already use mTLS — do not downgrade to plain HTTP in production.
  • The health endpoint (CORE_HEALTH_URL) runs on plain HTTP by design (port 8092). Restrict access to this port to the Message Center pod only via NetworkPolicy.

MongoDB Security

  1. Use authentication. The default development URI (mongodb://localhost:27018/core_admin) has no authentication — production must use credentials.

  2. Restrict network access. Only the Message Center pod should reach MongoDB port 27017. In Kubernetes, enforce this with NetworkPolicy.

  3. Enable TLS. Configure MongoDB to require TLS connections and set the URI to mongodb+srv://... or mongodb://...?tls=true.

  4. Least privilege. The MongoDB user for Message Center needs only readWrite on the $MONGODB_DB database. It does not need admin or clusterAdmin roles.


Session Security

  • Sessions are stored as HTTP-only, Secure, SameSite=Lax cookies managed by next-auth.
  • The session JWT expires after 8 hours (next-auth default). Users must re-authenticate after expiry.
  • Rate limiting is applied to the login endpoint — multiple failed attempts temporarily block the IP.
  • Passwords are hashed with bcrypt (cost factor 12). Password comparison is timing-safe via bcrypt's constant-time comparison.

Audit Logging

Message Center records compliance-grade audit logs for all significant actions (26 write-points). In a hardened deployment:

  1. Set AUDIT_RETENTION_DAYS according to your compliance requirements (e.g., 365 for one year).
  2. Back up audit_logs as part of your standard MongoDB backup — these records may be required for regulatory audits.
  3. Monitor [audit-fallback-overflow] — this event means compliance events are being lost.

Supply Chain Security

  • The Docker image is built from the official node:20-alpine base image.
  • All Node.js dependencies are pinned via yarn.lock and installed with --frozen-lockfile.
  • NEXT_TELEMETRY_DISABLED=1 is set in both builder and runner stages of the Dockerfile — no data is sent to Vercel.

Next Steps