Skip to content

feat: comprehensive security hardening for Nova#367

Open
Taure wants to merge 12 commits intomasterfrom
feat/session-cookie-hardening
Open

feat: comprehensive security hardening for Nova#367
Taure wants to merge 12 commits intomasterfrom
feat/session-cookie-hardening

Conversation

@Taure
Copy link
Collaborator

@Taure Taure commented Mar 12, 2026

Summary

  • Harden session cookie attributes (HttpOnly, Secure, SameSite=Lax)
  • Crypto-secure session ID generation
  • Add nova_secure_headers_plugin (CSP, X-Frame-Options, etc.)
  • Add nova_rate_limit_plugin for DoS/brute-force protection
  • Session lifecycle management (timeout, rotation, cleanup)
  • Tighten CORS plugin defaults from wildcard to explicit values
  • Add nova_force_ssl_plugin for HTTP→HTTPS redirect
  • Add nova_log_filter for sensitive parameter redaction
  • Fix XSS in error template (remove |safe filter)
  • Fix path traversal in nova_file_controller
  • Comprehensive security guide

Changes since review

Test plan

  • rebar3 eunit — 387 tests, 0 failures
  • rebar3 xref — clean
  • rebar3 dialyzer — clean

@Taure Taure changed the title fix: harden session cookie security attributes feat: comprehensive security hardening for Nova Mar 12, 2026
Copy link
Contributor

@burbas burbas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I see a potential problem here with rotate_session which don't comminicate the session_id with the user. The session gets a new ID, but can't be used cause the cookie is never updated (I might have missed something here?)

  • We need to take a larger discussion around which plugins we are including and which should be in a separate repo. But for now I think it's alright. Good additions!

Taure and others added 12 commits March 19, 2026 09:03
Set HttpOnly, Secure, SameSite=Lax, and Path=/ on all session cookies
to mitigate XSS cookie theft, man-in-the-middle attacks, and CSRF.
Replace rand:uniform/1 with crypto:strong_rand_bytes/1 for
cryptographically secure session ID generation. Cookie options are
configurable via the session_cookie_opts application environment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a configurable plugin that sets security headers on every response:
- X-Frame-Options: DENY (clickjacking protection)
- X-Content-Type-Options: nosniff (MIME sniffing protection)
- X-XSS-Protection: 1; mode=block
- Referrer-Policy: strict-origin-when-cross-origin
- Permissions-Policy: restrictive defaults

Optional HSTS and CSP support via plugin options.
All headers are overridable per-application.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sliding window rate limiter using ETS counters. Tracks requests per
client IP within configurable time windows. Returns 429 with
Retry-After header when limit exceeded.

Supports path-prefix filtering and custom key functions for
flexible rate limiting (e.g. per API key, per user).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add nova_session:rotate/1 for session fixation prevention — generates
  new session ID and migrates data on privilege changes (login/logout)
- Add rotate_session/2 callback to nova_session behaviour (optional)
- Add session timestamps (created_at, last_accessed) to ETS backend
- Add periodic cleanup of expired sessions (configurable intervals)
- session_max_age (default 24h) and session_idle_timeout (default 1h)
  configurable via application environment
- Backward-compatible with legacy {SessionId, Data} tuple format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change default Access-Control-Allow-Headers from "*" to
"Content-Type, Authorization" and Access-Control-Allow-Methods from
"*" to "GET, POST, PUT, DELETE, OPTIONS". Both are now configurable
via allow_headers and allow_methods plugin options.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redirects HTTP requests to HTTPS with 301 status. Supports custom
host/port override, query string preservation, and path exclusions
for health checks. Use alongside nova_secure_headers_plugin's HSTS
option for complete TLS enforcement.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OTP logger filter that redacts sensitive parameters from log output.
Default filtered keys: password, secret, token, api_key, authorization,
passwd, credit_card. Configurable via filter_parameters app env.

Supports case-insensitive partial matching, nested maps, and both
atom and binary keys. Can be used as a logger filter or called
directly via redact_params/1,2.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The extra_msg variable in nova_error.dtl was rendered with |safe,
bypassing erlydtl's auto-escaping. While the content is from internal
crash info, this is a defense-in-depth fix. Changed the error
controller to use newlines instead of <br /> HTML tags.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reject path segments containing ".." or "." in wildcard pathinfo
before joining with the static directory path. Without this check,
a request like /static/../../../etc/passwd could escape the
configured static directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers sessions, CSRF, HTTP headers, TLS, rate limiting, CORS, XSS,
SQL injection, authorization, logging, and BEAM-specific concerns.
Includes recommended plugin stack for production deployments and
links to ERLEF, Phoenix, and OWASP references.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The fallback path deletes the old session but cannot migrate data
since the behaviour has no get-all-values callback. Add a warning
log so this is visible.
@Taure Taure force-pushed the feat/session-cookie-hardening branch from df44bcc to c395daa Compare March 19, 2026 08:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants