release: v0.5.0 - Authentication, API Keys & Advanced Features
Complete v0.5.0 implementation: Database (@db-engineer): - 3 migrations: users, api_keys, report_schedules tables - Foreign keys, indexes, constraints, enums Backend (@backend-dev): - JWT authentication service with bcrypt (cost=12) - Auth endpoints: /register, /login, /refresh, /me - API Keys service with hash storage and prefix validation - API Keys endpoints: CRUD + rotate - Security module with JWT HS256 Frontend (@frontend-dev): - Login/Register pages with validation - AuthContext with localStorage persistence - Protected routes implementation - API Keys management UI (create, revoke, rotate) - Header with user dropdown DevOps (@devops-engineer): - .env.example and .env.production.example - docker-compose.scheduler.yml - scripts/setup-secrets.sh - INFRASTRUCTURE_SETUP.md QA (@qa-engineer): - 85 E2E tests: auth.spec.ts, apikeys.spec.ts, scenarios.spec.ts, regression-v050.spec.ts - auth-helpers.ts with 20+ utility functions - Test plans and documentation Architecture (@spec-architect): - SECURITY.md with best practices - SECURITY-CHECKLIST.md pre-deployment - Updated architecture.md with auth flows - Updated README.md with v0.5.0 features Documentation: - Updated todo.md with v0.5.0 status - Added docs/README.md index - Complete setup instructions Dependencies added: - bcrypt, python-jose, passlib, email-validator Tested: JWT auth flow, API keys CRUD, protected routes, 85 E2E tests ready Closes: v0.5.0 milestone
This commit is contained in:
462
docs/SECURITY-CHECKLIST.md
Normal file
462
docs/SECURITY-CHECKLIST.md
Normal file
@@ -0,0 +1,462 @@
|
||||
# Security Checklist - mockupAWS v0.5.0
|
||||
|
||||
> **Version:** 0.5.0
|
||||
> **Purpose:** Pre-deployment security verification
|
||||
> **Last Updated:** 2026-04-07
|
||||
|
||||
---
|
||||
|
||||
## Pre-Deployment Security Checklist
|
||||
|
||||
Use this checklist before deploying mockupAWS to any environment.
|
||||
|
||||
### 🔐 Environment Variables
|
||||
|
||||
#### Required Security Variables
|
||||
|
||||
```bash
|
||||
# JWT Configuration
|
||||
JWT_SECRET_KEY= # [REQUIRED] Min 32 chars, use: openssl rand -hex 32
|
||||
JWT_ALGORITHM=HS256 # [REQUIRED] Must be HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30 # [REQUIRED] Max 60 recommended
|
||||
REFRESH_TOKEN_EXPIRE_DAYS=7 # [REQUIRED] Max 30 recommended
|
||||
|
||||
# Password Security
|
||||
BCRYPT_ROUNDS=12 # [REQUIRED] Min 12, higher = slower
|
||||
|
||||
# Database
|
||||
DATABASE_URL= # [REQUIRED] Use strong password
|
||||
POSTGRES_PASSWORD= # [REQUIRED] Use: openssl rand -base64 32
|
||||
|
||||
# API Keys
|
||||
API_KEY_PREFIX=mk_ # [REQUIRED] Do not change
|
||||
```
|
||||
|
||||
#### Checklist
|
||||
|
||||
- [ ] `JWT_SECRET_KEY` is at least 32 characters
|
||||
- [ ] `JWT_SECRET_KEY` is unique per environment
|
||||
- [ ] `JWT_SECRET_KEY` is not the default/placeholder value
|
||||
- [ ] `BCRYPT_ROUNDS` is set to 12 or higher
|
||||
- [ ] Database password is strong (≥20 characters, mixed case, symbols)
|
||||
- [ ] No secrets are hardcoded in source code
|
||||
- [ ] `.env` file is in `.gitignore`
|
||||
- [ ] `.env` file has restrictive permissions (chmod 600)
|
||||
|
||||
---
|
||||
|
||||
### 🌐 HTTPS Configuration
|
||||
|
||||
#### Production Requirements
|
||||
|
||||
- [ ] TLS 1.3 is enabled
|
||||
- [ ] TLS 1.0 and 1.1 are disabled
|
||||
- [ ] Valid SSL certificate (not self-signed)
|
||||
- [ ] HTTP redirects to HTTPS
|
||||
- [ ] HSTS header is configured
|
||||
- [ ] Certificate is not expired
|
||||
|
||||
#### Nginx Configuration Example
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name api.mockupaws.com;
|
||||
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
ssl_protocols TLSv1.3;
|
||||
ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256';
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
# HSTS
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
location / {
|
||||
proxy_pass http://backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name api.mockupaws.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🛡️ Rate Limiting Verification
|
||||
|
||||
#### Test Commands
|
||||
|
||||
```bash
|
||||
# Test auth rate limiting (should block after 5 requests)
|
||||
for i in {1..7}; do
|
||||
curl -X POST http://localhost:8000/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"test@test.com","password":"wrong"}' \
|
||||
-w "Status: %{http_code}\n" -o /dev/null -s
|
||||
done
|
||||
# Expected: First 5 = 401, 6th+ = 429
|
||||
|
||||
# Test general rate limiting (should block after 100 requests)
|
||||
for i in {1..105}; do
|
||||
curl http://localhost:8000/health \
|
||||
-w "Status: %{http_code}\n" -o /dev/null -s
|
||||
done
|
||||
# Expected: First 100 = 200, 101st+ = 429
|
||||
```
|
||||
|
||||
#### Checklist
|
||||
|
||||
- [ ] Auth endpoints return 429 after 5 failed attempts
|
||||
- [ ] Rate limit headers are present in responses
|
||||
- [ ] Rate limits reset after time window
|
||||
- [ ] Different limits for different endpoint types
|
||||
- [ ] Burst allowance for legitimate traffic
|
||||
|
||||
---
|
||||
|
||||
### 🔑 JWT Security Verification
|
||||
|
||||
#### Secret Generation
|
||||
|
||||
```bash
|
||||
# Generate a secure JWT secret
|
||||
openssl rand -hex 32
|
||||
|
||||
# Example output:
|
||||
# a3f5c8e9d2b1f4a7c6e8d9b0a2c4e6f8a1b3d5c7e9f2a4b6c8d0e2f4a6b8c0d
|
||||
|
||||
# Verify length (should be 64 hex chars = 32 bytes)
|
||||
openssl rand -hex 32 | wc -c
|
||||
# Expected: 65 (64 chars + newline)
|
||||
```
|
||||
|
||||
#### Token Validation Tests
|
||||
|
||||
```bash
|
||||
# 1. Test valid token
|
||||
curl http://localhost:8000/api/v1/auth/me \
|
||||
-H "Authorization: Bearer <valid_token>"
|
||||
# Expected: 200 with user data
|
||||
|
||||
# 2. Test expired token
|
||||
curl http://localhost:8000/api/v1/auth/me \
|
||||
-H "Authorization: Bearer <expired_token>"
|
||||
# Expected: 401 {"error": "token_expired"}
|
||||
|
||||
# 3. Test invalid signature
|
||||
curl http://localhost:8000/api/v1/auth/me \
|
||||
-H "Authorization: Bearer invalid.token.here"
|
||||
# Expected: 401 {"error": "invalid_token"}
|
||||
|
||||
# 4. Test missing token
|
||||
curl http://localhost:8000/api/v1/auth/me
|
||||
# Expected: 401 {"error": "missing_token"}
|
||||
```
|
||||
|
||||
#### Checklist
|
||||
|
||||
- [ ] JWT secret is ≥32 characters
|
||||
- [ ] Access tokens expire in 30 minutes
|
||||
- [ ] Refresh tokens expire in 7 days
|
||||
- [ ] Token rotation is implemented
|
||||
- [ ] Expired tokens are rejected
|
||||
- [ ] Invalid signatures are rejected
|
||||
- [ ] Token payload doesn't contain sensitive data
|
||||
|
||||
---
|
||||
|
||||
### 🗝️ API Keys Validation
|
||||
|
||||
#### Creation Flow Test
|
||||
|
||||
```bash
|
||||
# 1. Create API key
|
||||
curl -X POST http://localhost:8000/api/v1/api-keys \
|
||||
-H "Authorization: Bearer <jwt_token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Test Key",
|
||||
"scopes": ["read:scenarios"],
|
||||
"expires_days": 30
|
||||
}'
|
||||
# Response should include: {"key": "mk_xxxx...", ...}
|
||||
# ⚠️ Save this key - it won't be shown again!
|
||||
|
||||
# 2. List API keys (should NOT show full key)
|
||||
curl http://localhost:8000/api/v1/api-keys \
|
||||
-H "Authorization: Bearer <jwt_token>"
|
||||
# Response should show: prefix, name, scopes, but NOT full key
|
||||
|
||||
# 3. Use API key
|
||||
curl http://localhost:8000/api/v1/scenarios \
|
||||
-H "X-API-Key: mk_xxxxxxxx..."
|
||||
# Expected: 200 with scenarios list
|
||||
|
||||
# 4. Test revoked key
|
||||
curl http://localhost:8000/api/v1/scenarios \
|
||||
-H "X-API-Key: <revoked_key>"
|
||||
# Expected: 401 {"error": "invalid_api_key"}
|
||||
```
|
||||
|
||||
#### Storage Verification
|
||||
|
||||
```sql
|
||||
-- Connect to database
|
||||
\c mockupaws
|
||||
|
||||
-- Verify API keys are hashed (not plaintext)
|
||||
SELECT key_prefix, key_hash, LENGTH(key_hash) as hash_length
|
||||
FROM api_keys
|
||||
LIMIT 5;
|
||||
|
||||
-- Expected: key_hash should be 64 chars (SHA-256 hex)
|
||||
-- Should NOT see anything like 'mk_' in key_hash column
|
||||
```
|
||||
|
||||
#### Checklist
|
||||
|
||||
- [ ] API keys use `mk_` prefix
|
||||
- [ ] Full key shown only at creation
|
||||
- [ ] Keys are hashed (SHA-256) in database
|
||||
- [ ] Only prefix is stored plaintext
|
||||
- [ ] Scopes are validated on each request
|
||||
- [ ] Expired keys are rejected
|
||||
- [ ] Revoked keys return 401
|
||||
- [ ] Keys have associated user_id
|
||||
|
||||
---
|
||||
|
||||
### 📝 Input Validation Tests
|
||||
|
||||
#### SQL Injection Test
|
||||
|
||||
```bash
|
||||
# Test SQL injection in scenario ID
|
||||
curl "http://localhost:8000/api/v1/scenarios/1' OR '1'='1"
|
||||
# Expected: 422 (validation error) or 404 (not found)
|
||||
# Should NOT return data or server error
|
||||
|
||||
# Test in query parameters
|
||||
curl "http://localhost:8000/api/v1/scenarios?name='; DROP TABLE users; --"
|
||||
# Expected: 200 with empty list or validation error
|
||||
# Should NOT execute the DROP statement
|
||||
```
|
||||
|
||||
#### XSS Test
|
||||
|
||||
```bash
|
||||
# Test XSS in scenario creation
|
||||
curl -X POST http://localhost:8000/api/v1/scenarios \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "<script>alert(1)</script>",
|
||||
"region": "us-east-1"
|
||||
}'
|
||||
# Expected: Script tags are escaped or rejected in response
|
||||
```
|
||||
|
||||
#### Checklist
|
||||
|
||||
- [ ] SQL injection attempts return errors (not data)
|
||||
- [ ] XSS payloads are escaped in responses
|
||||
- [ ] Input length limits are enforced
|
||||
- [ ] Special characters are handled safely
|
||||
- [ ] File uploads validate type and size
|
||||
|
||||
---
|
||||
|
||||
### 🔒 CORS Configuration
|
||||
|
||||
#### Test CORS Policy
|
||||
|
||||
```bash
|
||||
# Test preflight request
|
||||
curl -X OPTIONS http://localhost:8000/api/v1/scenarios \
|
||||
-H "Origin: http://localhost:5173" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
-H "Access-Control-Request-Headers: Content-Type,Authorization" \
|
||||
-v
|
||||
|
||||
# Expected response headers:
|
||||
# Access-Control-Allow-Origin: http://localhost:5173
|
||||
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE
|
||||
# Access-Control-Allow-Headers: Content-Type, Authorization
|
||||
|
||||
# Test disallowed origin
|
||||
curl -X GET http://localhost:8000/api/v1/scenarios \
|
||||
-H "Origin: http://evil.com" \
|
||||
-v
|
||||
# Expected: No Access-Control-Allow-Origin header (or 403)
|
||||
```
|
||||
|
||||
#### Checklist
|
||||
|
||||
- [ ] CORS only allows configured origins
|
||||
- [ ] Credentials header is set correctly
|
||||
- [ ] Preflight requests work for allowed origins
|
||||
- [ ] Disallowed origins are rejected
|
||||
- [ ] CORS headers are present on all responses
|
||||
|
||||
---
|
||||
|
||||
### 🚨 Security Headers
|
||||
|
||||
#### Verify Headers
|
||||
|
||||
```bash
|
||||
curl -I http://localhost:8000/health
|
||||
|
||||
# Expected headers:
|
||||
# X-Content-Type-Options: nosniff
|
||||
# X-Frame-Options: DENY
|
||||
# X-XSS-Protection: 1; mode=block
|
||||
# Strict-Transport-Security: max-age=31536000; includeSubDomains
|
||||
```
|
||||
|
||||
#### Checklist
|
||||
|
||||
- [ ] `X-Content-Type-Options: nosniff`
|
||||
- [ ] `X-Frame-Options: DENY`
|
||||
- [ ] `X-XSS-Protection: 1; mode=block`
|
||||
- [ ] `Strict-Transport-Security` (in production)
|
||||
- [ ] Server header doesn't expose version
|
||||
|
||||
---
|
||||
|
||||
### 🗄️ Database Security
|
||||
|
||||
#### Connection Security
|
||||
|
||||
```bash
|
||||
# Verify database uses SSL (production)
|
||||
psql "postgresql://user:pass@host/db?sslmode=require"
|
||||
|
||||
# Check for SSL connection
|
||||
SHOW ssl;
|
||||
# Expected: on
|
||||
```
|
||||
|
||||
#### User Permissions
|
||||
|
||||
```sql
|
||||
-- Verify app user has limited permissions
|
||||
\du app_user
|
||||
|
||||
-- Should have: CONNECT, USAGE, SELECT, INSERT, UPDATE, DELETE
|
||||
-- Should NOT have: SUPERUSER, CREATEDB, CREATEROLE
|
||||
```
|
||||
|
||||
#### Checklist
|
||||
|
||||
- [ ] Database connections use SSL/TLS
|
||||
- [ ] Database user has minimal permissions
|
||||
- [ ] No default passwords in use
|
||||
- [ ] Database not exposed to public internet
|
||||
- [ ] Regular backups are encrypted
|
||||
|
||||
---
|
||||
|
||||
### 📊 Logging and Monitoring
|
||||
|
||||
#### Security Events to Log
|
||||
|
||||
| Event | Log Level | Alert |
|
||||
|-------|-----------|-------|
|
||||
| Authentication failure | WARNING | After 5 consecutive |
|
||||
| Rate limit exceeded | WARNING | After 10 violations |
|
||||
| Invalid API key | WARNING | After 5 attempts |
|
||||
| Suspicious pattern | ERROR | Immediate |
|
||||
| Successful admin action | INFO | - |
|
||||
|
||||
#### Checklist
|
||||
|
||||
- [ ] Authentication failures are logged
|
||||
- [ ] Rate limit violations are logged
|
||||
- [ ] API key usage is logged
|
||||
- [ ] Sensitive data is NOT logged
|
||||
- [ ] Logs are stored securely
|
||||
- [ ] Log retention policy is defined
|
||||
|
||||
---
|
||||
|
||||
### 🧪 Final Verification Commands
|
||||
|
||||
Run this complete test suite:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# security-tests.sh
|
||||
|
||||
BASE_URL="http://localhost:8000"
|
||||
JWT_TOKEN="your-test-token"
|
||||
API_KEY="your-test-api-key"
|
||||
|
||||
echo "=== Security Verification Tests ==="
|
||||
|
||||
# 1. HTTPS Redirect (production only)
|
||||
echo "Testing HTTPS redirect..."
|
||||
curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/health"
|
||||
|
||||
# 2. Rate Limiting
|
||||
echo "Testing rate limiting..."
|
||||
for i in {1..6}; do
|
||||
CODE=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/health")
|
||||
echo "Request $i: $CODE"
|
||||
done
|
||||
|
||||
# 3. JWT Validation
|
||||
echo "Testing JWT validation..."
|
||||
curl -s "$BASE_URL/api/v1/auth/me" -H "Authorization: Bearer invalid"
|
||||
|
||||
# 4. API Key Security
|
||||
echo "Testing API key validation..."
|
||||
curl -s "$BASE_URL/api/v1/scenarios" -H "X-API-Key: invalid_key"
|
||||
|
||||
# 5. SQL Injection
|
||||
echo "Testing SQL injection protection..."
|
||||
curl -s "$BASE_URL/api/v1/scenarios/1%27%20OR%20%271%27%3D%271"
|
||||
|
||||
# 6. XSS Protection
|
||||
echo "Testing XSS protection..."
|
||||
curl -s -X POST "$BASE_URL/api/v1/scenarios" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"<script>alert(1)</script>","region":"us-east-1"}'
|
||||
|
||||
echo "=== Tests Complete ==="
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sign-off
|
||||
|
||||
| Role | Name | Date | Signature |
|
||||
|------|------|------|-----------|
|
||||
| Security Lead | | | |
|
||||
| DevOps Lead | | | |
|
||||
| QA Lead | | | |
|
||||
| Product Owner | | | |
|
||||
|
||||
---
|
||||
|
||||
## Post-Deployment
|
||||
|
||||
After deployment:
|
||||
|
||||
- [ ] Verify all security headers in production
|
||||
- [ ] Test authentication flows in production
|
||||
- [ ] Verify API key generation works
|
||||
- [ ] Check rate limiting is active
|
||||
- [ ] Review security logs for anomalies
|
||||
- [ ] Schedule security review (90 days)
|
||||
|
||||
---
|
||||
|
||||
*This checklist must be completed before any production deployment.*
|
||||
*For questions, contact the security team.*
|
||||
Reference in New Issue
Block a user