Skip to content

Infrastructure Security

HTTPS, cookies, CORS, security headers, database & secrets.

HTTPS Everywhere

# Force HTTPS
server {
    listen 80;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    # SSL configuration...
}

Secure Cookies

// Production cookie settings
res.cookie('auth_token', token, {
  httpOnly: true,     // No JavaScript access
  secure: true,       // HTTPS only
  sameSite: 'lax',    // CSRF protection
  maxAge: 3600000,    // 1 hour
});

CORS Configuration

// ✅ Specific origins
CORS_ORIGINS=https://app.example.com,https://admin.example.com

// ❌ Never in production
CORS_ORIGINS=*  // Allows any origin!

Security Headers

Essential HTTP headers to protect against common attacks:

Header Purpose Risk if Missing
HSTS Forces HTTPS Downgrade attacks
CSP Blocks XSS/injection Script injection
X-Frame-Options Prevents clickjacking UI redress attacks
X-Content-Type-Options Stops MIME sniffing Content confusion
Referrer-Policy Controls referrer info URL data leaks
Permissions-Policy Restricts browser features Feature abuse

Nginx Configuration

server {
    listen 443 ssl http2;
    server_name app.example.com;

    # HSTS - Force HTTPS for 1 year, include subdomains
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # CSP - Control what resources can load
    add_header Content-Security-Policy "
        default-src 'self';
        script-src 'self' https://cdn.example.com;
        style-src 'self' 'unsafe-inline';
        img-src 'self' data: https:;
        font-src 'self';
        connect-src 'self' https://api.authvital.com;
        frame-ancestors 'none';
        base-uri 'self';
        form-action 'self';
    " always;

    # Clickjacking protection
    add_header X-Frame-Options "DENY" always;

    # Prevent MIME type sniffing
    add_header X-Content-Type-Options "nosniff" always;

    # Control referrer information
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Restrict browser features
    add_header Permissions-Policy "
        accelerometer=(),
        camera=(),
        geolocation=(),
        gyroscope=(),
        magnetometer=(),
        microphone=(),
        payment=(),
        usb=()
    " always;
}

Header Details

HSTS (Strict-Transport-Security)

# Minimum recommended
add_header Strict-Transport-Security "max-age=31536000" always;

# With subdomains (recommended if all subdomains support HTTPS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

# Ready for browser preload list (most secure)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

CSP (Content-Security-Policy)

# ✅ Strict CSP - blocks most XSS attacks
add_header Content-Security-Policy "
    default-src 'self';
    script-src 'self';
    style-src 'self';
    img-src 'self' data:;
    connect-src 'self' https://api.authvital.com;
    frame-ancestors 'none';
" always;

# ⚠️ Development CSP - more permissive for debugging
add_header Content-Security-Policy "
    default-src 'self';
    script-src 'self' 'unsafe-inline' 'unsafe-eval';
    style-src 'self' 'unsafe-inline';
" always;

X-Frame-Options

# ✅ Completely deny framing (most secure)
add_header X-Frame-Options "DENY" always;

# ⚠️ Allow same-origin framing only
add_header X-Frame-Options "SAMEORIGIN" always;

Testing Your Headers

# Quick test with curl
curl -I https://your-app.com | grep -i "strict\|security\|frame\|content-type\|referrer\|permissions"

# Or use online tools:
# - https://securityheaders.com
# - https://observatory.mozilla.org

Database Security

# ✅ Strong passwords
DB_PASSWORD=long-random-string-32-chars-minimum

# ✅ Separate credentials per environment
# dev: authvital_dev / dev-password
# prod: authvital_prod / secure-prod-password

# ✅ Principle of least privilege
# App user shouldn't have DROP TABLE permissions

Secret Management

Environment Variables

# ✅ Use secret managers in production
# - Google Secret Manager
# - AWS Secrets Manager
# - HashiCorp Vault
# - Doppler

# ❌ Don't commit secrets
.env          # In .gitignore
.env.local    # In .gitignore

Key Rotation

# Rotate signing keys periodically (AuthVital does this automatically)
KEY_ROTATION_INTERVAL_SECONDS=604800  # 7 days

# ⚠️ Changing MASTER_SECRET invalidates all tokens!
# The stored signing keys can no longer be decrypted — users will need to re-login

Webhook Secrets

// ✅ Always verify webhook signatures (RSA-SHA256 via JWKS)
const router = new WebhookRouter({
  authVitalHost: process.env.AV_HOST,  // JWKS URL derived automatically
  handler: myHandler,
});

// ❌ Never skip signature verification
app.post('/webhooks', (req, res) => {
  // Missing signature check = anyone can call this!
  handleEvent(req.body);
});