MiniSearch / docs /security.md
github-actions[bot]
Sync from https://github.com/felladrin/MiniSearch
6ace587
|
Raw
History Blame Contribute Delete
5.14 kB
# Security
## Access Control
- **Optional Access Keys**: `ACCESS_KEYS` environment variable for usage restriction
- **Rate Limiting**: Applied to search and inference endpoints
- **Server-side Validation**: Access keys verified before proxying to SearXNG
- **Key Timeout**: `ACCESS_KEY_TIMEOUT_HOURS` controls cache duration
### Access Key Validation Flow
1. User enters access key on the **AccessPage** UI
2. Client hashes the key client-side using argon2id
3. Server validates the hash against configured `ACCESS_KEYS` via `validateAccessKeyServerHook`
4. On success: key hash is stored in localStorage with timestamp
5. On subsequent loads, `useAccessKeyValidation` in `App.tsx` calls `verifyStoredAccessKey()` to check if the cached key is still valid
6. If expired (based on `ACCESS_KEY_TIMEOUT_HOURS`), user is prompted to re-enter
Whether access keys are enabled is read at runtime from `/api/config`. When that
request fails, the app shell refuses to render rather than assuming access keys
are off, so a request that never arrives cannot skip the access key page.
### `/api/config` Exposure
`/api/config` is unauthenticated by design: the client needs it before it can
prove anything, and the access key page itself depends on it. It returns only
whether a feature is on plus its display defaults, and never returns
`ACCESS_KEYS`, `INTERNAL_OPENAI_COMPATIBLE_API_KEY`, or any other secret. Adding
a field to `ServerConfig` in `shared/serverConfig.ts` publishes it to anyone who
can reach the instance, so keep secrets out of that interface.
### Search Token Lifecycle
Every HTTP request from client to backend carries a `token` query parameter for CSRF protection:
1. **Token Generation**: On build/startup, `regenerateSearchToken()` writes a random token to `{os.tempdir()}/minisearch-token`, readable only by the user running the build (`0600`)
2. **Client Injection**: The token is injected as `VITE_SEARCH_TOKEN` compile-time constant via Vite's `define` option
3. **Per-Request Auth**: Client includes token as `?token=` parameter on all `/search/text` and `/search/images` requests
4. **Server Verification**: `handleTokenVerification()` in `searchEndpointServerHook.ts` validates the token before proxying to SearXNG
5. **Session Tracking**: Validated tokens are stored in an in-memory `Set<string>` (`verifiedTokens.ts`) for session counting
## Privacy
- **Local-First Storage**: All data stored in IndexedDB, no cloud sync
- **No Tracking**: No telemetry, analytics, or user tracking
- **SearXNG Integration**: All web searches routed through privacy-focused metasearch
- **No External Requests**: Optional browser-only mode for complete privacy
## Data Protection
- **Access Key Hashing**: Access keys hashed using argon2id before storage (via hash-wasm)
- **TTL-based Cleanup**: Automatic cleanup of cached data
- **No PII Collection**: No personally identifiable information stored
- **User Control**: Users can export and delete all their data
## Security Best Practices
- Input validation on all endpoints
- Sanitization of user-generated content
- Search token generation: a per-build/per-startup token written to a temp file (`server/searchToken.ts`), using 32 bytes from the `node:crypto` CSPRNG, with the file restricted to its owner (`0600`)
- HTTPS enforcement in production
- Regular dependency updates via Renovate
- **Argon2 Hashing**: Access keys hashed using argon2id for secure validation (not storage encryption)
- **Cross-Origin Isolation**: COOP/COEP headers for SharedArrayBuffer security
- **CSRF Protection**: Search tokens validated via argon2 hash comparison, over a 32-byte digest
## Server-Side Security Modules
| Module | Purpose |
|--------|---------|
| `server/searchToken.ts` | Reads/writes the CSRF token from `{tempdir}/minisearch-token` |
| `server/verifiedTokens.ts` | In-memory `Set<string>` of verified session tokens |
| `server/searchesSinceLastRestart.ts` | In-memory counters for abuse monitoring |
| `server/searchEndpointServerHook.ts` | Proxies text/image search to SearXNG after token verification (via `handleTokenVerification`) |
| `server/verifyTokenAndRateLimit.ts` | Verifies the Argon2 token hash and enforces rate limiting (10 requests per 10 seconds) shared by search and inference endpoints |
| `server/handleTokenVerification.ts` | Middleware bridge that calls `verifyTokenAndRateLimit` and writes 400/401/429 error responses for the search and inference endpoints |
| `server/configEndpointServerHook.ts` | Serves the non-secret runtime config at `/api/config`, including whether access keys are enabled |
## Threat Model
- **Local Environment**: Assumes trusted local execution
- **Network Requests**: All external requests go through SearXNG proxy
- **AI Models**: Models run locally or through trusted providers
- **Data Exfiltration**: Prevented by local-first architecture
## Related Topics
- **Configuration**: `docs/configuration.md` - Environment variables for access control
- **Overview**: `docs/overview.md` - Security architecture and data flow
- **AI Integration**: `docs/ai-integration.md` - Privacy implications of inference types