This document describes the security features and configuration options in Peek.
Peek includes several layers of security protection:
- CORS Configuration - Control which origins can access the API
- Rate Limiting - Prevent API abuse and DoS attacks
- Security Headers - Protect against common web vulnerabilities
- Input Validation - Sanitize and validate all user input
- Error Sanitization - Prevent credential leakage in logs
- Request Tracing - Audit trail with unique request IDs
By default, Peek allows requests from any origin (for development convenience). For production deployments, restrict CORS using the ALLOWED_ORIGINS environment variable:
# Single origin
ALLOWED_ORIGINS=https://dash.example.com
# Multiple origins (comma-separated)
ALLOWED_ORIGINS=https://dash.example.com,https://dashboard.local,http://192.168.1.100:8080When ALLOWED_ORIGINS is not set, all origins are allowed.
The API implements rate limiting to prevent abuse:
| Endpoint Type | Limit | Window |
|---|---|---|
General API (/api/*) |
100 requests | 15 minutes |
Authentication (/api/ring-auth, /api/homeconnect-auth) |
10 requests | 1 minute |
Data Fetching (/api/data/*, /api/weather/*) |
200 requests | 1 minute |
All API responses include rate limit information:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1706745600
When rate limited, the API returns a 429 Too Many Requests response:
{
"error": "Too many API requests, please try again later",
"retryAfter": 300
}The following security headers are set on all responses:
| Header | Value | Purpose |
|---|---|---|
X-Content-Type-Options |
nosniff |
Prevent MIME type sniffing |
X-Frame-Options |
SAMEORIGIN |
Clickjacking protection |
X-XSS-Protection |
1; mode=block |
XSS filter (legacy browsers) |
Referrer-Policy |
strict-origin-when-cross-origin |
Control referrer info |
Permissions-Policy |
geolocation=(), microphone=(), camera=() |
Restrict browser APIs |
Content-Security-Policy |
See below | XSS and injection protection |
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: https: http:;
font-src 'self' data:;
connect-src 'self';
frame-ancestors 'self';
Note: 'unsafe-inline' is required for Tailwind CSS styles. Image sources allow external URLs to support integration icons.
Every request is assigned a unique ID for tracing and debugging:
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
This ID is logged with all related operations, making it easy to trace issues across the system.
Search parameters are validated and limited:
- Maximum length: 255 characters
- Trimmed of whitespace
- Used in parameterized SQL queries (SQL injection safe)
File uploads are restricted:
- Allowed types: PNG, JPEG, GIF, SVG, WebP, ICO
- Maximum file size: 10 MB per file
- Maximum files per upload: 100
All database queries use parameterized statements:
// Safe - parameterized query
db.exec('SELECT * FROM integrations WHERE id = ?', [id]);
// Never done - string interpolation
// db.exec(`SELECT * FROM integrations WHERE id = '${id}'`);Error messages are sanitized before logging to prevent credential leakage:
// Patterns redacted in logs:
// - token=xxx, token: xxx
// - password=xxx, password: xxx
// - api_key=xxx, apikey=xxx
// - Bearer xxx, Basic xxx
// - secret=xxx, authorization=xxxInternal error details are not exposed to clients. Instead, generic error messages are returned:
{
"error": "Failed to fetch integration data"
}Detailed error information is logged server-side with the request ID for debugging.
Integration connections support SSL verification configuration:
{
host: '192.168.1.100',
port: 443,
verifySSL: true // Set to false only for self-signed certificates
}Integration credentials are stored in the SQLite database. For production deployments with sensitive data, consider:
- Using API tokens instead of passwords where possible
- Rotating credentials regularly
- Restricting database file permissions
- Backing up the database securely
- Set
ALLOWED_ORIGINSto restrict CORS - Use HTTPS with a valid certificate
- Run behind a reverse proxy (nginx, Traefik, Caddy)
- Restrict network access to the dashboard
- Use strong passwords/tokens for integrations
- Keep the Docker image updated
- Monitor logs for suspicious activity
Example nginx configuration:
server {
listen 443 ssl http2;
server_name dash.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}If you discover a security vulnerability, please report it responsibly by opening a private issue or contacting the maintainers directly. Do not disclose security issues publicly until they have been addressed.