Skip to content

Secure Deployment Guide ​

This guide outlines the security hardening measures required for production deployments of RustChat.

Table of Contents ​

  1. Critical Security Configuration
  2. Token Transport Security
  3. Rate Limiting
  4. Secret Management
  5. TLS and Network Security
  6. Monitoring and Alerting

Critical Security Configuration ​

Environment Variables ​

The following environment variables MUST be set for secure production deployments:

bash
# Required: Strong random secrets (min 32 characters, high entropy)
RUSTCHAT_JWT_SECRET="your-256-bit-secret-min-32-chars-long-random"
RUSTCHAT_ENCRYPTION_KEY="another-256-bit-secret-for-encryption-ops"

# Required: Set to production
RUSTCHAT_ENVIRONMENT=production

# Required: Secure OAuth token delivery (one-time codes only)
RUSTCHAT_SECURITY_OAUTH_TOKEN_DELIVERY=cookie

# Recommended: Enable rate limiting (enabled by default)
RUSTCHAT_SECURITY_RATE_LIMIT_ENABLED=true

Secret Generation ​

Generate cryptographically secure secrets using:

bash
# Using OpenSSL (recommended)
RUSTCHAT_JWT_SECRET=$(openssl rand -base64 48)
RUSTCHAT_ENCRYPTION_KEY=$(openssl rand -base64 48)

# Using /dev/urandom
RUSTCHAT_JWT_SECRET=$(head -c 48 /dev/urandom | base64)

IMPORTANT: Secrets must:

  • Be at least 32 characters long
  • Have high entropy (no dictionary words, sequential characters)
  • Be different from each other (JWT_SECRET ≠ ENCRYPTION_KEY)
  • Be rotated periodically

Token Transport Security ​

WebSocket Authentication ​

RustChat accepts authentication tokens via:

  1. Authorization header: Authorization: Bearer xyz (✅ Secure)
  2. Sec-WebSocket-Protocol header (✅ Secure)

Query-token authentication is removed and rejected at startup if RUSTCHAT_SECURITY_WS_ALLOW_QUERY_TOKEN=true.

WebSocket connections MUST use secure headers:

javascript
const ws = new WebSocket('wss://chat.example.com/api/v1/ws');
// Token is sent in the handshake headers, not visible in URL

OAuth Token Delivery ​

Only secure mode is supported: RUSTCHAT_SECURITY_OAUTH_TOKEN_DELIVERY=cookie.

  • One-time exchange code in URL: /login?code=xyz
  • Client exchanges code for token via POST /api/v1/oauth2/exchange
  • Token never appears in redirect URLs

Rate Limiting ​

Rate limiting is enabled by default and protects against brute-force attacks.

Configuration ​

bash
# Enable/disable rate limiting (default: true)
RUSTCHAT_SECURITY_RATE_LIMIT_ENABLED=true

# Auth endpoints (login, register) - per IP
RUSTCHAT_SECURITY_RATE_LIMIT_AUTH_PER_MINUTE=10

# WebSocket connections - per IP
RUSTCHAT_SECURITY_RATE_LIMIT_WS_PER_MINUTE=30

Rate Limit Responses ​

When rate limited, the API returns:

http
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
Retry-After: 45

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Please try again later.",
    "retry_after": 45
  }
}

Secret Management ​

Startup Validation ​

RustChat validates secrets on startup:

In Production (RUSTCHAT_ENVIRONMENT=production):

  • Fails to start if secrets are weak
  • Checks minimum length (32 chars)
  • Checks entropy (no repeated patterns)
  • Checks for common weak patterns

In Development:

  • Logs warnings for weak secrets
  • Allows startup with weak secrets

Secret Rotation ​

To rotate secrets:

  1. JWT Secret Rotation:

    bash
    # 1. Generate new secret
    NEW_JWT_SECRET=$(openssl rand -base64 48)
    
    # 2. Deploy with new secret (existing tokens become invalid)
    # 3. Users must re-authenticate
  2. Encryption Key Rotation:

    bash
    # Requires data re-encryption - contact support

TLS and Network Security ​

Required TLS Configuration ​

nginx
# nginx example
server {
    listen 443 ssl http2;
    server_name chat.example.com;
    
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    
    # Security headers
    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    
    location / {
        proxy_pass http://rustchat_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        # Forward client IP for rate limiting
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

CORS Configuration ​

In production, explicitly set allowed origins:

bash
RUSTCHAT_CORS_ALLOWED_ORIGINS="https://chat.example.com,https://admin.example.com"

Never use wildcard (*) in production.


Monitoring and Alerting ​

Security Events to Monitor ​

EventSeverityAction
Rate limit exceededWarningMonitor for patterns
Invalid OAuth stateWarningPossible CSRF attempt
Weak secret detectedCriticalRotate immediately
WebSocket auth failureInfoNormal for expired tokens
Exchange code reuseWarningPossible token theft

Log Redaction ​

Configure your reverse proxy to redact sensitive headers:

nginx
# nginx - don't log tokens
log_format security '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

access_log /var/log/nginx/access.log security;

Deployment Checklist ​

Before going to production:

  • [ ] Set RUSTCHAT_ENVIRONMENT=production
  • [ ] Generate strong JWT_SECRET (≥32 chars, random)
  • [ ] Generate strong ENCRYPTION_KEY (different from JWT_SECRET)
  • [ ] Set RUSTCHAT_SECURITY_OAUTH_TOKEN_DELIVERY=cookie
  • [ ] Configure CORS with specific origins
  • [ ] Enable TLS 1.2+ only
  • [ ] Configure rate limiting
  • [ ] Set up log redaction
  • [ ] Configure security headers
  • [ ] Test OAuth flow end-to-end
  • [ ] Verify WebSocket connections work with headers

Migration from Insecure Defaults ​

If currently running with insecure defaults:

  1. Immediate (P0): Rotate to strong secrets
  2. Week 1: Update frontend to use header-based WebSocket auth
  3. Week 2: Update frontend to support OAuth exchange codes
  4. Week 3: Deploy with OAUTH_TOKEN_DELIVERY=cookie

Contact your security team if you need assistance with the migration.

RustChat Documentation