-
Notifications
You must be signed in to change notification settings - Fork 1
SECURITY
- 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
- 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
ADMIN_PASSWORD=your-password-here
ADMIN_PASSWORD_IS_HASHED=false# 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" 10Then add to .env.local:
ADMIN_PASSWORD='$2b$10$...' # Your bcrypt hash
ADMIN_PASSWORD_IS_HASHED=trueThe middleware automatically adds the following security headers to protected routes:
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-origin-
Permissions-Policy: camera=(), microphone=(), geolocation=()(no browser feature access)
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-srcallowsws:/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 forX-Frame-Options: DENYand 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-securitySessions 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.
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/*
- Always use a strong, unique password in production
- Rotate passwords regularly
- Use environment variables, never commit passwords to git
- Enable HTTPS in production deployments
- Monitor authentication logs for suspicious activity
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
- 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
- Multi-factor authentication
- Role-based access control
- Redis-based session storage (optional)
- Hardware key support (WebAuthn)
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
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
Version: 1.0.0 | Updated: 2026-09-14 12:29 UTC | GitHub