Skip to content

SECURITY

GitHub Actions Bot edited this page Jun 10, 2026 · 8 revisions

Security Documentation

Authentication

Password Storage

  • Passwords are hashed using bcrypt with a cost factor of 10
  • Plain text passwords are automatically hashed on first use
  • Supports both plain and pre-hashed passwords in environment variables

Session Management

  • Sessions use secure httpOnly cookies (not localStorage)
  • Cookies are marked as SameSite=strict to prevent CSRF
  • Sessions expire after 24 hours
  • Secure flag is set in production environments

Configuration

Using Plain Password (Development)

ADMIN_PASSWORD=your-password-here
ADMIN_PASSWORD_IS_HASHED=false

Using Hashed Password (Production)

# Generate hash using the provided script
node scripts/hash-password.js

# Or manually with bcryptjs
npm install -g bcryptjs-cli
bcryptjs-cli hash "your-password" 10

Then add to .env.local:

ADMIN_PASSWORD='$2b$10$...' # Your bcrypt hash
ADMIN_PASSWORD_IS_HASHED=true

Security Headers

The middleware automatically adds the following security headers to protected routes:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • X-XSS-Protection: 1; mode=block
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy: camera=(), microphone=(), geolocation=() (no browser feature access)

Content Security Policy

Admin ships with a strict CSP applied via next.config.mjs headers. The policy denies inline scripts in production, restricts script sources to self, and prevents the page from being framed by any other origin.

default-src 'self';
script-src 'self' 'wasm-unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob:;
font-src 'self' data:;
connect-src 'self' ws: wss:;
frame-ancestors 'none';
form-action 'self';
base-uri 'self';
object-src 'none';

Notes:

  • 'unsafe-inline' for styles is required by Tailwind's CSS-in-JS injection. Removing it would require a CSP nonce on every style block.
  • connect-src allows ws: / wss: so the Socket.io live-update layer works in dev (ws://localhost:3021) and in production (wss://...).
  • frame-ancestors 'none' is a stricter modern replacement for X-Frame-Options: DENY and is enforced by every browser that supports CSP level 2+.

To verify the policy in a running container:

curl -sI http://localhost:3021/login | grep -i content-security

Session Cookies: Attribute Reference

Sessions are stored in httpOnly cookies issued by the auth layer. Each cookie carries:

Attribute Value Why
HttpOnly true JavaScript cannot read the cookie. Protects against XSS-based session theft.
SameSite Strict Cookie is never sent on cross-origin requests. Hard CSRF stop.
Secure true (production) Cookie is only sent over HTTPS. Set automatically when NODE_ENV=production.
Path / Available to the whole admin app.
Max-Age 86400 (24h) / 604800 (7d if Remember Me) Bounded session lifetime.

The Secure flag is gated on NODE_ENV so local dev over plain HTTP still works while production deployments cannot leak the cookie over an unencrypted channel. CSRF defense is layered: SameSite=Strict cookie attribute + origin validation on all mutating routes.

Protected Routes

All API routes under the following paths require authentication:

  • /api/docker/*
  • /api/services/*
  • /api/database/*
  • /api/config/*
  • /api/system/*
  • /api/project/*
  • /api/nself/*
  • /api/storage/*
  • /api/monitoring/*
  • /api/graphql/*
  • /api/redis/*
  • /api/cli/*

Best Practices

  1. Always use a strong, unique password in production
  2. Rotate passwords regularly
  3. Use environment variables, never commit passwords to git
  4. Enable HTTPS in production deployments
  5. Monitor authentication logs for suspicious activity

Session Storage (v0.0.7+)

Sessions are now stored in LokiJS embedded database (nadmin.db):

  • Sessions persist across restarts
  • Automatic TTL expiration (7 days by default)
  • Session activity tracking and extension
  • Audit logging for login attempts

Security Features (v0.0.7+)

  • CSRF Protection: SameSite=strict cookies + origin validation
  • Rate Limiting: Built-in rate limiting on authentication endpoints
  • Input Validation: Full validation using Zod schemas
  • Shell Injection Prevention: execFile() with array arguments instead of exec()
  • SQL Injection Prevention: Parameterized queries and identifier validation
  • Path Traversal Prevention: Resolved path validation

Future Improvements

  • Multi-factor authentication
  • Role-based access control
  • Redis-based session storage (optional)
  • Hardware key support (WebAuthn)

Security Audit

For a full security audit of the nself-admin codebase, see SECURITY_AUDIT.

The audit covers:

  • OWASP Top 10 vulnerabilities
  • Command injection risks
  • Authentication and session management
  • Input validation
  • Dependency vulnerabilities
  • Remediation recommendations

Route Auth Gap Audit (2026-06-08, P1-E6-W18-S18-T02)

All 447 HTTP route handlers in src/app/api/ were audited against the middleware and handler-level auth chain.

Finding Count Status
CRITICAL (no auth) 0 None found
MEDIUM (session only, no CSRF on mutations) 5 Fix tickets FIX-M-01, FIX-M-02 pending
LOW (GET-only, session via middleware) 155 Acceptable
Justified public routes 9 By design (bootstrap/login flow)

Key finding: middleware validates session for all non-public API routes. Handler-level requireAuth() adds CSRF for mutating methods. Five mutating routes (auth/pairing, bus-factor, plugins/github/sync) use middleware session check only — no CSRF. Marked MEDIUM, fix tickets created.

Full audit: .claude/docs/security/admin-route-auth-audit.md

Clone this wiki locally