Security Hardening¶
Secure your New Hires Reporting System deployment for production.
Production Security Checklist¶
- [ ] HTTPS enabled with valid SSL certificate
- [ ] Services bind to localhost only (127.0.0.1)
- [ ] Reverse proxy configured
- [ ] Debug mode disabled (
DEBUG=false) - [ ] AWS credentials stored securely in
.env - [ ] File permissions restricted (
.env= 600) - [ ]
.envfile not shared or exposed - [ ] Docker containers run as non-root (future)
- [ ] Regular security updates applied
- [ ] Firewall configured
- [ ] Rate limiting enabled (reverse proxy)
- [ ] Access logs monitored
- [ ] Database backups encrypted and secure
- [ ] AWS IAM least-privilege permissions
Network Security¶
Firewall Configuration¶
# Allow only necessary ports
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp # HTTP (redirect to HTTPS)
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
Localhost Binding (Production)¶
Ensure services only accept local connections:
# docker-compose.prod.yml
ports:
- "127.0.0.1:8000:8000" # Backend API
- "127.0.0.1:8080:8080" # Frontend (React + Nginx)
Never expose directly:
# ❌ DON'T DO THIS IN PRODUCTION
ports:
- "8000:8000" # Exposes backend to internet
- "8080:8080" # Exposes frontend to internet
Database (already internal-only):
# ✅ Good: No port mapping (internal only)
db:
# No ports exposed - only accessible within Docker network
Application Security¶
Disable Debug Mode¶
Debug mode risks: - Exposes stack traces - Shows internal paths - Reveals configuration - Provides detailed error messages - Leaks AWS credentials in logs
Credentials Management¶
# Secure .env permissions
chmod 600 .env
chown your-user:your-user .env
# Verify permissions
ls -la .env
# Should show: -rw------- (600)
# Keep .env file secure
# - Never share in emails or chat
# - Never commit to version control
# - Store backup in secure password manager
# - Use different credentials per environment
Rotate AWS Credentials¶
# Every 90 days:
# 1. Create new AWS access key in IAM console
# 2. Test new key in staging first
# 3. Update production .env with new credentials
# 4. Restart services to load new credentials
# 5. Verify workers can connect to Bedrock
# 6. Delete old access key in IAM console
# Rotation script example:
# 1. Create new key
aws iam create-access-key --user-name newhires-app
# 2. Update .env
sed -i 's/AWS_ACCESS_KEY_ID=.*/AWS_ACCESS_KEY_ID=NEW_KEY/' .env
sed -i 's/AWS_SECRET_ACCESS_KEY=.*/AWS_SECRET_ACCESS_KEY=NEW_SECRET/' .env
# 3. Restart
docker-compose -f docker-compose.prod.yml restart workers
# 4. Verify
docker logs newhires-workers --tail=20
# 5. Delete old key
aws iam delete-access-key --access-key-id OLD_KEY --user-name newhires-app
HTTPS/TLS Security¶
Force HTTPS¶
Nginx configuration:
# Redirect HTTP to HTTPS
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}
Strong TLS Configuration¶
# Modern TLS only
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; includeSubDomains; preload" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
Security Headers¶
Add to reverse proxy:
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self'" always;
CORS Security¶
Backend configuration (production):
# Allow only your frontend domain
origins = ["https://your-domain.com"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # Not "*"
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Content-Type", "Authorization"],
)
File Upload Security¶
Size Limits¶
File Type Validation¶
System validates: - ✅ Plain text files only - ✅ Fixed-width format - ✅ UTF-8 encoding - ❌ No executables - ❌ No archives - ❌ No images - ❌ No scripts
Rate Limiting¶
Nginx Rate Limiting¶
# Limit requests per IP
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=upload_limit:10m rate=5r/m;
server {
# API endpoints
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
}
# File uploads (stricter limit)
location /api/v1/files {
limit_req zone=upload_limit burst=2 nodelay;
}
}
Monitoring & Logging¶
Enable Access Logs¶
# Log all requests
access_log /var/log/nginx/newhires-access.log;
error_log /var/log/nginx/newhires-error.log;
Monitor for Suspicious Activity¶
# Watch for repeated failures
sudo tail -f /var/log/nginx/access.log | grep "40[134]"
# High request rates
sudo tail -f /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn
# Check for unusual user agents
sudo tail -f /var/log/nginx/access.log | grep -i -E "bot|crawler|scanner"
Docker Security¶
Run as Non-Root (Future)¶
Scan Images for Vulnerabilities¶
# Using Trivy
trivy image 878796852397.dkr.ecr.us-east-1.amazonaws.com/newhires-backend:latest
# Using Docker Scout
docker scout cves 878796852397.dkr.ecr.us-east-1.amazonaws.com/newhires-backend:latest
Limit Container Resources¶
# In docker-compose.prod.yml
services:
workers:
deploy:
resources:
limits:
memory: 2G
cpus: '2.0'
reservations:
memory: 1G
cpus: '1.0'
Secrets Management¶
Environment Variables Only¶
# ✅ Good: .env file with AWS credentials
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
POSTGRES_PASSWORD=SecurePassword123!
# ❌ Bad: Hardcoded in application code
access_key = "AKIA..." # Never do this
# ❌ Bad: Shared via email or chat
# Never send .env files through insecure channels
# ❌ Bad: Committed to version control
# Always add .env to .gitignore
AWS IAM Best Practices¶
Least Privilege Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "BedrockModelInvocationOnly",
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel"
],
"Resource": [
"arn:aws:bedrock:us-east-1::foundation-model/us.anthropic.claude-sonnet-4-5-20250929-v1:0",
"arn:aws:bedrock:us-east-1::foundation-model/us.meta.llama4-scout-17b-instruct-v1:0"
]
}
]
}
Don't grant:
- ❌ bedrock:* (too broad)
- ❌ bedrock:CreateModel (not needed)
- ❌ Administrator access
- ❌ Access to other AWS services unless required
Future: Secrets Manager¶
For enterprise deployments, consider:
- AWS Secrets Manager - Integrates with AWS Bedrock
- HashiCorp Vault - Multi-cloud support
- Azure Key Vault - Azure deployments
- Google Secret Manager - GCP deployments
Data Protection¶
Sensitive Data Handling¶
The system processes: - ✅ Employee names - ✅ SSNs (Social Security Numbers) - ✅ Addresses - ✅ Employer data - ✅ Validation errors and corrections
Protections: - ✅ HTTPS encryption in transit - ✅ PostgreSQL database with authentication - ✅ Database stored in Docker volume (encrypted at rest optional) - ✅ No data sent to third parties (except AWS Bedrock for corrections) - ✅ Regular database backups - ⚠️ AWS Bedrock processes correction data (see AWS data handling policies)
Data Retention: - Files: Stored until manually deleted - Corrections: Stored in database - Logs: Rotated based on configuration (default: 3 files × 10MB)
AWS Bedrock Data Privacy¶
When using AWS Bedrock: - Data sent: Only specific record fields needing correction - AWS retention: AWS does not store or train on Bedrock API requests - Compliance: AWS Bedrock is HIPAA eligible and SOC 2 compliant - Region: Data stays in us-east-1 region
Compliance Considerations¶
If handling sensitive data:
- GDPR: EU data protection (right to deletion, data minimization)
- CCPA: California privacy (disclosure requirements)
- HIPAA: If health data involved (AWS Bedrock supports BAA)
- SOC 2: Security auditing (AWS Bedrock is SOC 2 certified)
- PCI DSS: If payment data involved (not typical for this use case)
Recommendations: - Document data flows and retention - Implement data deletion procedures - Maintain audit logs - Conduct privacy impact assessments - Review AWS Bedrock compliance documentation
Database Security¶
PostgreSQL Security¶
# Strong database password in .env
POSTGRES_PASSWORD=$(openssl rand -base64 32)
# Verify database is not exposed
docker ps | grep newhires-db
# Should show: 5432/tcp (no 0.0.0.0 binding)
# Encrypted backups
docker exec newhires-db pg_dump -U newhires newhires | \
gpg --symmetric --cipher-algo AES256 > backup_$(date +%Y%m%d).sql.gpg
# Test backup integrity regularly
gpg --decrypt backup_20241124.sql.gpg | head -20
Database Access Control¶
-- Verify only newhires user exists
SELECT usename FROM pg_user;
-- Check database permissions
\l+ newhires
-- Review table permissions
\dp correction_jobs
Incident Response¶
Security Incident Plan¶
- Detect: Monitor logs and alerts
- Check access logs for unusual patterns
- Monitor AWS CloudTrail for Bedrock API anomalies
-
Review worker logs for errors
-
Contain: Isolate affected systems
- Disable compromised AWS credentials immediately
- Stop affected containers:
docker-compose -f docker-compose.prod.yml down -
Block suspicious IPs in firewall
-
Investigate: Analyze logs and access
- Export all logs for analysis
- Review AWS CloudTrail logs
-
Check for data exfiltration
-
Remediate: Patch vulnerabilities
- Rotate all credentials
- Update Docker images
-
Apply security patches
-
Document: Record incident details
- Timeline of events
- Impact assessment
-
Actions taken
-
Review: Update procedures
- Update security policies
- Improve monitoring
- Train team on lessons learned
Emergency Contacts¶
Document: - Security team contacts - AWS Support (if Enterprise support plan) - On-call procedures - Escalation paths - Communication templates
Regular Maintenance¶
Security Update Schedule¶
| Task | Frequency | Command |
|---|---|---|
| OS security patches | Weekly | sudo apt update && sudo apt upgrade |
| Docker image updates | Monthly | Update IMAGE_TAG in .env |
| AWS credential rotation | Every 90 days | See rotation script above |
| SSL certificate renewal | Auto | Let's Encrypt auto-renewal |
| Dependency updates | Monthly | Rebuild Docker images |
| Security audit | Quarterly | Full security review |
| Backup test restore | Monthly | Test database restore |
| Vulnerability scan | Weekly | trivy image ... |
Vulnerability Scanning¶
Automated Scans¶
# Weekly cron job
0 2 * * 0 trivy image --severity HIGH,CRITICAL \
878796852397.dkr.ecr.us-east-1.amazonaws.com/newhires-backend:latest \
> /var/log/security-scan.log
# Scan workers too
0 2 * * 0 trivy image --severity HIGH,CRITICAL \
878796852397.dkr.ecr.us-east-1.amazonaws.com/newhires-workers:latest \
>> /var/log/security-scan.log
Best Practices Summary¶
Network & Access¶
- ✅ Always use HTTPS in production
- ✅ Bind services to localhost only
- ✅ Use reverse proxy (Nginx/Caddy)
- ✅ Configure firewall (ufw/iptables)
- ✅ Implement rate limiting
Application¶
- ✅ Disable debug mode in production
- ✅ Use strong TLS configuration
- ✅ Add security headers
- ✅ Restrict CORS properly
- ✅ Validate file uploads
Credentials & Secrets¶
- ✅ Use AWS IAM least-privilege policies
- ✅ Rotate AWS credentials every 90 days
- ✅ Secure .env file (chmod 600)
- ✅ Never commit secrets to git
- ✅ Use different credentials per environment
Data & Database¶
- ✅ Use strong database passwords
- ✅ Backup database regularly
- ✅ Encrypt backups
- ✅ Test restore procedures
- ✅ Document data retention policies
Monitoring¶
- ✅ Monitor access logs
- ✅ Set up AWS CloudTrail logging
- ✅ Scan for vulnerabilities
- ✅ Configure security alerts
- ✅ Review logs regularly
Maintenance¶
- ✅ Keep systems updated
- ✅ Patch security vulnerabilities
- ✅ Update Docker images monthly
- ✅ Rotate credentials quarterly
- ✅ Document procedures
Security Audit Checklist¶
### Network
- [ ] Firewall configured
- [ ] Only necessary ports open (80, 443, 22)
- [ ] Services bind to localhost (8000, 8080)
- [ ] Reverse proxy properly configured
- [ ] HTTPS enforced
### Application
- [ ] Debug mode disabled
- [ ] LOG_LEVEL set to INFO or WARNING
- [ ] Security headers configured
- [ ] CORS properly restricted
- [ ] Rate limiting enabled
- [ ] File upload limits set
### AWS & Credentials
- [ ] AWS IAM least-privilege policy
- [ ] AWS credentials in .env only
- [ ] .env file permissions = 600
- [ ] .env not committed to git
- [ ] Credentials rotated in last 90 days
- [ ] Old credentials revoked
### Database
- [ ] Strong PostgreSQL password
- [ ] Database not exposed externally
- [ ] Regular backups configured
- [ ] Backup encryption enabled
- [ ] Restore tested recently
### Monitoring
- [ ] Access logs enabled
- [ ] Error logs monitored
- [ ] AWS CloudTrail enabled
- [ ] Security scanning automated
- [ ] Alerts configured
- [ ] Incident response plan documented
### Updates & Maintenance
- [ ] OS patches applied
- [ ] Docker images updated to latest IMAGE_TAG
- [ ] Dependencies current
- [ ] SSL certificates valid
- [ ] Vulnerability scans clean
Next Steps¶
- Upgrading - Update system to new versions
- Health Monitoring - Monitor production health
- AWS Bedrock Setup - Review IAM policies
- Backup & Restore - Database backup procedures