11 KiB
Security Best Practices Guide
This guide covers security configurations and best practices for deploying and operating Readur in production environments.
Authentication and Authorization
Authentication Setup
Readur supports multiple authentication methods to secure your document management system:
Local Authentication
# Basic authentication configuration
AUTH_SECRET: "your-secure-random-secret-min-32-chars"
SESSION_SECRET: "your-session-secret-min-32-chars"
Generate secure secrets:
# Generate auth secret
openssl rand -hex 32
# Generate session secret
openssl rand -base64 32
OIDC/SSO Integration
Configure OpenID Connect for enterprise SSO:
OIDC_ENABLED: "true"
OIDC_CLIENT_ID: "readur-client"
OIDC_CLIENT_SECRET: "your-client-secret"
OIDC_ISSUER_URL: "https://auth.example.com/realms/readur"
OIDC_REDIRECT_URI: "https://readur.example.com/api/auth/oidc/callback"
OIDC_SCOPES: "openid profile email"
Role-Based Access Control
Readur implements three user roles with distinct permissions:
| Role | Permissions |
|---|---|
| Admin | Full system access, user management, configuration changes |
| Editor | Document upload, edit, delete, OCR management |
| Viewer | Read-only access to documents and search |
Configure default role for new users:
DEFAULT_USER_ROLE: "viewer"
AUTO_CREATE_USERS: "false"
Session Management
Configure session security parameters:
SESSION_TIMEOUT: 3600 # Seconds (1 hour)
SESSION_COOKIE_SECURE: "true" # HTTPS only
SESSION_COOKIE_HTTPONLY: "true" # Prevent XSS
SESSION_COOKIE_SAMESITE: "strict" # CSRF protection
File Upload Security
Size Limits
Prevent resource exhaustion attacks:
MAX_FILE_SIZE_MB: 100 # Maximum file size
MAX_FILES_PER_UPLOAD: 10 # Batch upload limit
TOTAL_STORAGE_QUOTA_GB: 1000 # Per-user quota
File Type Validation
Restrict allowed file types:
ALLOWED_FILE_TYPES: "pdf,png,jpg,jpeg,txt,doc,docx"
BLOCK_EXECUTABLE_FILES: "true"
SCAN_FOR_MALWARE: "true" # Requires ClamAV integration
Upload Validation
// Example validation implementation
fn validate_upload(file: &UploadedFile) -> Result<(), SecurityError> {
// Check file size
if file.size > MAX_FILE_SIZE {
return Err(SecurityError::FileTooLarge);
}
// Validate MIME type
let detected_mime = magic::from_buffer(&file.data)?;
if !ALLOWED_MIME_TYPES.contains(&detected_mime) {
return Err(SecurityError::InvalidFileType);
}
// Check for malicious content
if contains_suspicious_patterns(&file.data) {
return Err(SecurityError::MaliciousContent);
}
Ok(())
}
Network Security
TLS Configuration
Always use HTTPS in production:
server {
listen 443 ssl http2;
ssl_certificate /etc/ssl/certs/readur.crt;
ssl_certificate_key /etc/ssl/private/readur.key;
# Modern TLS configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
}
CORS Configuration
Configure Cross-Origin Resource Sharing:
CORS_ENABLED: "true"
CORS_ALLOWED_ORIGINS: "https://app.example.com,https://admin.example.com"
CORS_ALLOWED_METHODS: "GET,POST,PUT,DELETE,OPTIONS"
CORS_MAX_AGE: 3600
Rate Limiting
Prevent abuse and DoS attacks:
RATE_LIMIT_ENABLED: "true"
RATE_LIMIT_REQUESTS_PER_MINUTE: 100
RATE_LIMIT_BURST_SIZE: 20
RATE_LIMIT_EXCLUDE_PATHS: "/health,/metrics"
Secrets Management
Environment Variables
Never commit secrets to version control:
# .env.example (commit this)
DATABASE_URL=postgresql://user:password@localhost/readur
AUTH_SECRET=change-this-secret
S3_ACCESS_KEY=your-access-key
S3_SECRET_KEY=your-secret-key
# .env (don't commit - add to .gitignore)
DATABASE_URL=postgresql://readur:SecurePass123!@db.internal/readur_prod
AUTH_SECRET=a8f7d9s8f7sd9f87sd9f87sd9f8s7df98s7df98s7df9
S3_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE
S3_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Secret Rotation
Implement regular secret rotation:
#!/bin/bash
# rotate-secrets.sh
# Generate new secrets
NEW_AUTH_SECRET=$(openssl rand -hex 32)
NEW_SESSION_SECRET=$(openssl rand -base64 32)
# Update application configuration
kubectl create secret generic readur-secrets \
--from-literal=auth-secret="$NEW_AUTH_SECRET" \
--from-literal=session-secret="$NEW_SESSION_SECRET" \
--dry-run=client -o yaml | kubectl apply -f -
# Restart application
kubectl rollout restart deployment/readur
Vault Integration
For production, use HashiCorp Vault or similar:
VAULT_ENABLED: "true"
VAULT_ADDR: "https://vault.internal:8200"
VAULT_TOKEN: "s.xxxxxxxxxxxxxxxx"
VAULT_PATH: "secret/data/readur"
Data Encryption
Encryption at Rest
Database encryption:
-- PostgreSQL transparent data encryption
ALTER SYSTEM SET ssl = on;
ALTER SYSTEM SET ssl_cert_file = '/etc/postgresql/server.crt';
ALTER SYSTEM SET ssl_key_file = '/etc/postgresql/server.key';
S3 storage encryption:
S3_ENCRYPTION_ENABLED: "true"
S3_ENCRYPTION_TYPE: "AES256" # or "aws:kms"
S3_KMS_KEY_ID: "arn:aws:kms:region:account:key/xxxxx"
Encryption in Transit
All data transmissions must be encrypted:
# Database connections
DATABASE_SSL_MODE: "require"
DATABASE_SSL_CERT: "/etc/ssl/certs/db-cert.pem"
# S3 connections
S3_USE_SSL: "true"
S3_VERIFY_SSL: "true"
# Redis connections (if used)
REDIS_TLS_ENABLED: "true"
REDIS_TLS_CERT: "/etc/ssl/certs/redis-cert.pem"
Audit Logging
Comprehensive Audit Trail
Configure audit logging for security events:
AUDIT_LOG_ENABLED: "true"
AUDIT_LOG_LEVEL: "info"
AUDIT_LOG_PATH: "/var/log/readur/audit.log"
AUDIT_LOG_FORMAT: "json"
Audit log entry structure:
{
"timestamp": "2025-01-15T10:30:45Z",
"event_type": "document_access",
"user_id": "user123",
"user_email": "user@example.com",
"action": "download",
"resource": "document/abc123",
"ip_address": "192.168.1.100",
"user_agent": "Mozilla/5.0...",
"result": "success",
"metadata": {
"document_name": "financial_report.pdf",
"file_size": 1048576
}
}
Events to Audit
Critical events that must be logged:
- User authentication (success/failure)
- User registration
- Password changes
- Document uploads/downloads
- Document deletions
- Permission changes
- Configuration changes
- Admin actions
- Failed authorization attempts
- Suspicious activities
Common Vulnerabilities and Mitigations
SQL Injection Prevention
Always use parameterized queries:
// Safe query using sqlx
let document = sqlx::query_as!(
Document,
"SELECT * FROM documents WHERE id = $1 AND user_id = $2",
document_id,
user_id
)
.fetch_one(&pool)
.await?;
// Never do this!
// let query = format!("SELECT * FROM documents WHERE id = {}", id);
XSS Prevention
Sanitize all user input:
use ammonia::clean;
fn sanitize_input(input: &str) -> String {
// Remove potentially dangerous HTML
clean(input)
}
// Content Security Policy headers
app.use(
DefaultHeaders::new()
.header("Content-Security-Policy", "default-src 'self'")
.header("X-Content-Type-Options", "nosniff")
.header("X-Frame-Options", "DENY")
.header("X-XSS-Protection", "1; mode=block")
);
CSRF Protection
Implement CSRF tokens:
// Generate CSRF token
let csrf_token = generate_csrf_token(&session);
// Validate on form submission
if !validate_csrf_token(&request.csrf_token, &session) {
return Err(SecurityError::InvalidCSRFToken);
}
Directory Traversal Prevention
Validate file paths:
use std::path::{Path, Component};
fn safe_path(user_input: &str) -> Result<PathBuf, SecurityError> {
let path = Path::new(user_input);
// Check for directory traversal attempts
for component in path.components() {
match component {
Component::ParentDir => return Err(SecurityError::PathTraversal),
Component::RootDir => return Err(SecurityError::AbsolutePath),
_ => {}
}
}
Ok(path.to_path_buf())
}
Security Headers
Configure security headers in your reverse proxy:
# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
Security Checklist
Pre-Deployment Checklist
- All secrets are properly managed (not in code)
- TLS/SSL certificates are valid and configured
- Authentication is properly configured
- File upload restrictions are in place
- Rate limiting is enabled
- CORS is properly configured
- Security headers are set
- Audit logging is enabled
- Database connections use SSL
- Default passwords are changed
- Unnecessary ports are closed
- Error messages don't leak sensitive information
Regular Security Tasks
- Review audit logs weekly
- Update dependencies monthly
- Rotate secrets quarterly
- Conduct security assessments annually
- Test backup restoration procedures
- Review user permissions
- Monitor for suspicious activities
- Update TLS certificates before expiry
Security Monitoring
Key Metrics to Monitor
# Prometheus alerts
groups:
- name: security
rules:
- alert: HighFailedLoginRate
expr: rate(auth_failures_total[5m]) > 10
annotations:
summary: "High rate of failed login attempts"
- alert: UnusualFileUploadVolume
expr: rate(file_uploads_total[1h]) > 100
annotations:
summary: "Unusual file upload activity detected"
- alert: SuspiciousAPIUsage
expr: rate(api_requests_total{status="403"}[5m]) > 20
annotations:
summary: "High rate of forbidden API requests"
Incident Response
Security Incident Procedure
- Detection: Identify the security incident
- Containment: Isolate affected systems
- Investigation: Determine scope and impact
- Eradication: Remove the threat
- Recovery: Restore normal operations
- Lessons Learned: Document and improve
Emergency Contacts
Maintain an updated contact list:
- Security team lead
- System administrators
- Database administrators
- Cloud provider support
- Legal counsel (if needed)