feat(security): add configurable security headers middleware

- Add SecurityHeadersMiddleware with HSTS, CSP, X-Frame-Options, X-Content-Type-Options
- Add configuration options in app/config.py
- Integrate middleware into app/main.py
- Add comprehensive tests in tests/test_security_headers.py
- Update .env.demo with security header examples
- Update docs/DeploymentGuide.md with security headers section and Traefik/Nginx examples
- Update docs/ConfigurationGuide.md with detailed configuration reference
- Update SECURITY_AUDIT.md to mark security headers implementation complete

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-10 14:05:19 +00:00
parent ec44cb082e
commit e144fdd50a
9 changed files with 870 additions and 5 deletions
+104
View File
@@ -98,6 +98,110 @@ DocuElevate can monitor multiple IMAP mailboxes for document attachments. Each m
| `AUTHENTIK_CONFIG_URL` | Configuration URL for Authentik OpenID Connect. |
| `OAUTH_PROVIDER_NAME` | Display name for the OAuth provider button. |
### Security Headers
DocuElevate supports HTTP security headers to improve browser-side security. These headers are enabled by default but should be disabled if your reverse proxy (Traefik, Nginx, etc.) already adds them. See [Deployment Guide - Security Headers](DeploymentGuide.md#security-headers) for detailed configuration examples.
#### Master Control
| **Variable** | **Description** | **Default** |
|-----------------------------|-------------------------------------------------------------------------|-------------|
| `SECURITY_HEADERS_ENABLED` | Enable/disable security headers middleware. Set to `false` if reverse proxy handles headers. | `true` |
#### Strict-Transport-Security (HSTS)
Forces browsers to use HTTPS for all future requests to this domain. **Only effective over HTTPS.**
| **Variable** | **Description** | **Default** |
|--------------------------------|--------------------------------------------------------------|------------------------------------------|
| `SECURITY_HEADER_HSTS_ENABLED` | Enable HSTS header. | `true` |
| `SECURITY_HEADER_HSTS_VALUE` | HSTS header value (max-age in seconds, subdomain support). | `max-age=31536000; includeSubDomains` |
**Common Values:**
- `max-age=31536000; includeSubDomains` (1 year, recommended for production)
- `max-age=300` (5 minutes, for testing)
- `max-age=63072000; includeSubDomains; preload` (2 years with HSTS preload)
#### Content-Security-Policy (CSP)
Controls which resources browsers are allowed to load. Helps prevent XSS attacks and code injection.
| **Variable** | **Description** | **Default** |
|-------------------------------|--------------------------------------------------------------|------------------------------------------|
| `SECURITY_HEADER_CSP_ENABLED` | Enable CSP header. | `true` |
| `SECURITY_HEADER_CSP_VALUE` | CSP policy directives. | See below |
**Default Policy:**
```
default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;
```
**Common Customizations:**
```bash
# Stricter CSP (remove 'unsafe-inline', use nonces)
SECURITY_HEADER_CSP_VALUE="default-src 'self'; script-src 'self'; style-src 'self';"
# Allow specific external domains
SECURITY_HEADER_CSP_VALUE="default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline';"
```
**Note:** The default policy includes `'unsafe-inline'` for compatibility with Tailwind CSS and inline JavaScript. For stricter security, use nonces or hashes.
#### X-Frame-Options
Prevents the page from being loaded in frames/iframes. Protects against clickjacking attacks.
| **Variable** | **Description** | **Default** |
|------------------------------------------|------------------------------------------|-------------|
| `SECURITY_HEADER_X_FRAME_OPTIONS_ENABLED` | Enable X-Frame-Options header. | `true` |
| `SECURITY_HEADER_X_FRAME_OPTIONS_VALUE` | X-Frame-Options header value. | `DENY` |
**Valid Values:**
- `DENY` - Page cannot be displayed in a frame (most secure)
- `SAMEORIGIN` - Page can only be displayed in a frame on the same origin
- `ALLOW-FROM uri` - Page can only be displayed in a frame on the specified origin (deprecated in modern browsers)
#### X-Content-Type-Options
Prevents browsers from MIME-sniffing responses away from the declared content-type. Helps prevent XSS attacks.
| **Variable** | **Description** | **Default** |
|-------------------------------------------------|------------------------------------------|-------------|
| `SECURITY_HEADER_X_CONTENT_TYPE_OPTIONS_ENABLED` | Enable X-Content-Type-Options header. | `true` |
**Note:** This header is always set to `nosniff` when enabled (no configuration needed).
#### Configuration Examples
**Direct Deployment (No Reverse Proxy):**
```bash
# Enable all security headers (default)
SECURITY_HEADERS_ENABLED=true
SECURITY_HEADER_HSTS_ENABLED=true
SECURITY_HEADER_CSP_ENABLED=true
SECURITY_HEADER_X_FRAME_OPTIONS_ENABLED=true
SECURITY_HEADER_X_CONTENT_TYPE_OPTIONS_ENABLED=true
```
**Behind Reverse Proxy (Traefik, Nginx):**
```bash
# Disable security headers (let proxy handle them)
SECURITY_HEADERS_ENABLED=false
```
**Custom Configuration:**
```bash
# Enable headers but customize values
SECURITY_HEADERS_ENABLED=true
SECURITY_HEADER_HSTS_VALUE="max-age=300" # 5 minutes for testing
SECURITY_HEADER_X_FRAME_OPTIONS_VALUE="SAMEORIGIN" # Allow same-origin framing
SECURITY_HEADER_CSP_VALUE="default-src 'self'; script-src 'self' https://trusted-cdn.com;"
```
**See Also:**
- [Deployment Guide - Security Headers](DeploymentGuide.md#security-headers) for Traefik/Nginx examples
- [SECURITY_AUDIT.md](../SECURITY_AUDIT.md#infrastructure-security) for security rationale
### OpenAI & Azure Document Intelligence
| **Variable** | **Description** | **How to Obtain** |
+118
View File
@@ -54,6 +54,124 @@ Access the web interface at `http://localhost:8000` and the API documentation at
## Production Considerations
### Security Headers
DocuElevate includes built-in support for HTTP security headers to improve browser-side security. These headers are enabled by default but can be configured based on your deployment scenario.
#### Supported Security Headers
- **Strict-Transport-Security (HSTS)**: Forces browsers to use HTTPS for all future requests
- **Content-Security-Policy (CSP)**: Controls which resources browsers are allowed to load
- **X-Frame-Options**: Prevents the page from being loaded in frames (clickjacking protection)
- **X-Content-Type-Options**: Prevents browsers from MIME-sniffing responses
#### Direct Deployment (No Reverse Proxy)
If you're running DocuElevate directly without a reverse proxy, security headers are enabled by default:
```bash
# In .env file
SECURITY_HEADERS_ENABLED=true
SECURITY_HEADER_HSTS_ENABLED=true
SECURITY_HEADER_CSP_ENABLED=true
SECURITY_HEADER_X_FRAME_OPTIONS_ENABLED=true
SECURITY_HEADER_X_CONTENT_TYPE_OPTIONS_ENABLED=true
```
**Note**: HSTS only works when serving content over HTTPS. If using HTTP for development, you can disable it:
```bash
SECURITY_HEADER_HSTS_ENABLED=false
```
#### Reverse Proxy Deployment (Traefik, Nginx, etc.)
When deploying behind a reverse proxy that adds security headers, **disable the built-in headers** to avoid duplication:
```bash
# In .env file
SECURITY_HEADERS_ENABLED=false
```
##### Traefik Configuration Example
Traefik can add security headers using middleware. Create a `docker-compose.yaml` with Traefik labels:
```yaml
services:
api:
labels:
- "traefik.enable=true"
- "traefik.http.routers.docuelevate.rule=Host(`docuelevate.example.com`)"
- "traefik.http.routers.docuelevate.entrypoints=websecure"
- "traefik.http.routers.docuelevate.tls=true"
- "traefik.http.routers.docuelevate.tls.certresolver=letsencrypt"
# Security headers middleware
- "traefik.http.routers.docuelevate.middlewares=security-headers@docker"
- "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.security-headers.headers.contentSecurityPolicy=default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;"
- "traefik.http.middlewares.security-headers.headers.customFrameOptionsValue=DENY"
- "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
```
Then set `SECURITY_HEADERS_ENABLED=false` in your `.env` file.
##### Nginx Configuration Example
Add security headers to your Nginx configuration:
```nginx
server {
listen 443 ssl http2;
server_name docuelevate.example.com;
# SSL configuration
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
location / {
proxy_pass http://localhost:8000;
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;
}
}
```
Then set `SECURITY_HEADERS_ENABLED=false` in your `.env` file.
#### Customizing Security Headers
You can customize individual header values in your `.env` file:
```bash
# Customize HSTS (e.g., shorter duration for testing)
SECURITY_HEADER_HSTS_VALUE="max-age=300"
# Customize CSP (e.g., allow specific external domains)
SECURITY_HEADER_CSP_VALUE="default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline';"
# Allow framing from same origin
SECURITY_HEADER_X_FRAME_OPTIONS_VALUE="SAMEORIGIN"
```
#### Security Considerations
1. **HSTS and HTTPS**: HSTS only works over HTTPS. Ensure you have a valid SSL certificate before enabling HSTS.
2. **CSP Testing**: The default CSP policy allows inline scripts and styles for compatibility. Test thoroughly before tightening.
3. **Content-Security-Policy**: The default policy allows `'unsafe-inline'` for scripts and styles for compatibility with Tailwind CSS and inline JavaScript. For stricter security, consider using nonces or hashes.
4. **X-Frame-Options**: Set to `DENY` by default. Change to `SAMEORIGIN` if you need to embed DocuElevate in iframes on the same domain.
See the [Configuration Guide](ConfigurationGuide.md) for all security header options.
### Reverse Proxy Setup
For production use, we recommend setting up a reverse proxy (like Nginx or Traefik) to handle HTTPS and domain routing: