204 lines
6.6 KiB
Markdown
204 lines
6.6 KiB
Markdown
# Authentication and security design
|
|
|
|
## Threat posture
|
|
|
|
The service is private and self-hosted but must assume:
|
|
|
|
- the reverse-proxy endpoint may become internet reachable;
|
|
- an authenticated low-privilege account can be malicious;
|
|
- imported Markdown and files are untrusted;
|
|
- database or backup theft is possible;
|
|
- API tokens can leak;
|
|
- container compromise must not automatically expose the host.
|
|
|
|
The complete threat register is in `docs/26-threat-model.md`.
|
|
|
|
## First-run owner setup
|
|
|
|
First-run setup is intentionally available without a separate installation code. The
|
|
endpoint remains a tightly bounded one-time operation:
|
|
|
|
1. Expose only that setup is required.
|
|
2. Require the new workspace and owner credentials over the setup form.
|
|
3. Protect the mutation with CSRF validation and a setup-specific rate limit.
|
|
4. Atomically create installation, workspace, owner and settings.
|
|
5. Permanently disable first-run registration after successful completion.
|
|
6. Audit successful completion without recording passwords or tokens.
|
|
|
|
The setup endpoint must not be exposed through an internet-facing reverse proxy before
|
|
owner setup is complete. Complete setup over the trusted LAN or HTTPS endpoint, then
|
|
configure the reverse proxy and create the first backup.
|
|
|
|
## Password authentication
|
|
|
|
Use ASP.NET Core Identity or an equivalent reviewed implementation.
|
|
|
|
Policy:
|
|
|
|
- minimum 12 characters by default;
|
|
- allow password-manager generated long passphrases;
|
|
- maximum input bound high enough for passphrases;
|
|
- no arbitrary composition rules;
|
|
- check against a small bundled common-password denylist where feasible without an
|
|
outbound service;
|
|
- adaptive, versioned password hashing;
|
|
- transparent rehash on successful login when policy changes;
|
|
- generic authentication failure;
|
|
- exponential/threshold lockout;
|
|
- session/security stamp invalidation after password reset or account disable.
|
|
|
|
## Operator-issued recovery
|
|
|
|
No email provider is required.
|
|
|
|
1. Administrator or local operator issues a single-use recovery token.
|
|
2. Token has at least 256 bits of entropy, a short expiry and one-use state.
|
|
3. Store only a verifier.
|
|
4. Deliver through an operator-visible channel.
|
|
5. Prefer a URL fragment so the raw token never enters access logs:
|
|
`https://knowledge.example/#/recover/<token>`.
|
|
6. Client posts the token body to the API.
|
|
7. Successful reset consumes the token and invalidates sessions.
|
|
8. Record safe audit metadata.
|
|
|
|
For the final administrator, provide a documented container operator command that creates
|
|
a recovery token without database surgery.
|
|
|
|
## Browser sessions
|
|
|
|
- Secure cookie in HTTPS production.
|
|
- HttpOnly.
|
|
- SameSite=Lax or stricter based on tested flows.
|
|
- Bounded idle and absolute lifetime.
|
|
- Session rotation after authentication/privilege change.
|
|
- Anti-forgery token on all cookie-authenticated mutations.
|
|
- No auth tokens in localStorage.
|
|
- Logout invalidates server-side session/security state where implemented.
|
|
- Concurrent session view/revocation is desirable in M1.
|
|
|
|
## Authorization
|
|
|
|
Authorization is workspace- and resource-aware.
|
|
|
|
- Reader: view/search permitted content.
|
|
- Editor: reader plus content/project creation and editing.
|
|
- Administrator: editor plus users, imports, backup, retention, audit and service accounts.
|
|
- Service account: scopes plus explicitly assigned workspace/content access; no role
|
|
inheritance.
|
|
|
|
Rules are enforced in Application use cases and query specifications. Endpoint attributes
|
|
alone are insufficient.
|
|
|
|
Tests cover:
|
|
|
|
- unauthenticated access;
|
|
- role escalation;
|
|
- object-ID substitution;
|
|
- cross-workspace access even though UI exposes one workspace;
|
|
- trashed/private content;
|
|
- attachment and revision access;
|
|
- project context aggregation;
|
|
- disabled users and revoked tokens.
|
|
|
|
## Service-account tokens
|
|
|
|
Format contains a non-secret public ID/prefix and a random secret. Store only a verifier.
|
|
|
|
- Show once.
|
|
- Optional expiry.
|
|
- Multiple independently revocable tokens per account.
|
|
- Named purpose and last-used time.
|
|
- Explicit scopes.
|
|
- Immediate revocation.
|
|
- Constant-time verification.
|
|
- Rate limit failed token authentication.
|
|
- Never accept token in query string.
|
|
- Never log the authorization header.
|
|
|
|
A database compromise should not make offline token recovery feasible because secrets are
|
|
uniform random 256-bit values.
|
|
|
|
## CSRF, CORS and origins
|
|
|
|
- Browser API is same-origin in production.
|
|
- CORS disabled unless a specific trusted integration requires it.
|
|
- Anti-forgery for cookie mutation.
|
|
- Bearer-token calls do not use cookies.
|
|
- Validate configured public/base URL.
|
|
- Proxy headers accepted only from configured trusted proxy networks.
|
|
- Host filtering enabled.
|
|
|
|
## Content security
|
|
|
|
Recommended production headers:
|
|
|
|
- Content-Security-Policy with no unsafe inline script;
|
|
- `frame-ancestors 'none'` unless explicitly required;
|
|
- `object-src 'none'`;
|
|
- `base-uri 'self'`;
|
|
- `form-action 'self'`;
|
|
- HSTS at reverse proxy after HTTPS validation;
|
|
- X-Content-Type-Options;
|
|
- Referrer-Policy;
|
|
- Permissions-Policy.
|
|
|
|
Rendered Markdown disallows raw HTML and unsafe URLs. Mermaid uses strict mode. Uploads
|
|
are not served as executable same-origin content.
|
|
|
|
## Secrets and encryption
|
|
|
|
- Secrets enter through mounted secret files or environment variables.
|
|
- ASP.NET data-protection keys persist outside the read-only root filesystem.
|
|
- Production data-protection keys should be protected with an operator-managed secret
|
|
where supported.
|
|
- Backup encryption is operator-configurable and must not rely on a key stored only inside
|
|
the backup.
|
|
- API tokens, passwords and bootstrap/recovery secrets are never recoverable from the DB.
|
|
- Do not pretend application-level encryption protects against a fully compromised
|
|
running application; document the threat boundary.
|
|
|
|
## Logging and privacy
|
|
|
|
Never log:
|
|
|
|
- passwords;
|
|
- raw tokens;
|
|
- cookies/session IDs;
|
|
- CSRF tokens;
|
|
- page bodies by default;
|
|
- attachment content;
|
|
- full recovery/bootstrap links;
|
|
- database connection strings.
|
|
|
|
Use safe IDs, correlation IDs and redacted structured properties. Audit retention and
|
|
diagnostic-log retention are separate.
|
|
|
|
## Rate limits and abuse controls
|
|
|
|
At minimum isolate policies for:
|
|
|
|
- setup/login/recovery;
|
|
- failed bearer authentication;
|
|
- global search;
|
|
- upload;
|
|
- import/export/backup;
|
|
- expensive project-context requests.
|
|
|
|
Return a generic response that does not confirm account/content existence.
|
|
|
|
## Security verification
|
|
|
|
Release requires:
|
|
|
|
- dependency vulnerability scan;
|
|
- secret scan;
|
|
- container scan;
|
|
- authorization matrix tests;
|
|
- CSRF tests;
|
|
- XSS corpus;
|
|
- malicious upload/path corpus;
|
|
- session/token lifecycle tests;
|
|
- security header test;
|
|
- non-root/read-only container evidence;
|
|
- independent security review.
|