Security Deployment Checklist¶
Cesivi Server - Pre-Deployment Security Verification Version: 1.0 Last Updated: 2026-01-18 Plan: PLAN-148 Phase 2.3
Quick Checklist¶
Use this checklist to verify security before EVERY production deployment.
✅ Authentication & Authorization¶
- [ ]
Cesivi:Authentication:AcceptAllCredentialsisfalsein appsettings.Production.json - [ ]
Cesivi:Authentication:AllowAnonymousisfalse - [ ]
Cesivi:Authentication:EnableBasicisfalse(prevents plaintext passwords) - [ ] At least one secure authentication method enabled (NTLM or JWT)
- [ ] Environment variable check will throw exception if
AcceptAllCredentials=truein Production - [ ] All API controllers have
[Authorize]attribute (or documented reason if[AllowAnonymous])
Verification Command:
# Check appsettings.Production.json
cat appsettings.Production.json | grep -A 5 "Authentication"
# Should output:
# "AcceptAllCredentials": false,
# "AllowAnonymous": false,
# "EnableBasic": false
✅ Network Security¶
- [ ] HTTPS enabled (
Cesivi:UseHttps: true) - [ ] Valid TLS certificate configured (not self-signed)
- [ ] TLS 1.2 or 1.3 minimum (check Kestrel configuration)
- [ ] HTTP redirects to HTTPS (or HTTP disabled entirely)
- [ ] Reverse proxy configured (nginx, IIS, Azure Application Gateway, etc.)
- [ ] CORS policy configured properly (not
AllowAnyOrigin())
Verification Command:
# Test HTTPS endpoint
curl -I https://your-domain.com/health
# Should return 200 OK with security headers
✅ Rate Limiting & Request Limits¶
- [ ] Rate limiting enabled (
IpRateLimiting:EnableEndpointRateLimiting: true) - [ ] Production rate limits configured (check appsettings.Production.json)
- [ ] General rate limit: ≤ 5 requests/second per IP
- [ ] API rate limit: ≤ 120 requests/minute per IP for
/_api/* - [ ] POST rate limit: ≤ 60 requests/minute per IP for POST operations
- [ ] Health endpoints whitelisted (no rate limiting for
/health,/ready,/live) - [ ] Request size limit configured (
Cesivi:RequestLimits:MaxRequestBodySize) - [ ] CSOM batch size limit configured (
Cesivi:RequestLimits:MaxCsomBatchSize) - [ ] File upload size limit appropriate (
Cesivi:RequestLimits:MaxFileUploadSize)
Verification Command:
# Test rate limiting (should return 429 after limit exceeded)
for i in {1..20}; do curl -I https://your-domain.com/_api/web; done
# Should see 429 Too Many Requests after configured limit
✅ Secrets & Configuration¶
- [ ]
FormDigest:SharedSecretchanged from default value - [ ] SharedSecret is at least 32 characters (recommend 64+)
- [ ] Redis connection string stored in environment variable (not appsettings.json)
- [ ] Database connection strings stored in environment variables
- [ ] TLS certificate password stored in environment variable (if applicable)
- [ ]
.envfile added to.gitignore(never commit secrets to git) - [ ] No secrets visible in
appsettings.Production.json(only placeholders likeREPLACE_WITH_...)
Verification Command:
# Check for secrets in configuration file (should return REPLACE_WITH_...)
cat appsettings.Production.json | grep -i "secret\|password\|connectionstring"
# Verify environment variables are set
echo $CESIVI_FORMDIGEST__SHAREDSECRET
echo $CESIVI_DISTRIBUTEDSTATE__CONNECTIONSTRING
✅ Infrastructure¶
- [ ] Redis used for distributed state (
DistributedState:Provider: "Redis") - [ ] Redis connection uses SSL/TLS (
ssl=truein connection string) - [ ] Redis requires password authentication
- [ ] Database backups configured and tested (restore tested within last 30 days)
- [ ] Data encrypted at rest (TDE for SQL Server, encrypted file system, etc.)
- [ ] Log files stored outside application directory (prevent unauthorized access)
- [ ] Separate storage for uploaded files (not in web root)
Verification Command:
# Test Redis connection (should require password)
redis-cli -h your-redis-host ping
# Should return: (error) NOAUTH Authentication required.
# Test Redis with password
redis-cli -h your-redis-host -a YOUR_PASSWORD ping
# Should return: PONG
✅ Monitoring & Logging¶
- [ ] Centralized logging configured (ELK, Splunk, Azure Monitor, CloudWatch)
- [ ] Security alerts configured (failed logins, rate limits, errors)
- [ ] Log level set to
WarningorInformationin production (notDebug) - [ ] Log rotation configured (prevent disk fill)
- [ ] No secrets logged (check log output for passwords, tokens, API keys)
- [ ] Structured logging enabled (JSON format for log aggregation)
- [ ] Health check endpoints exposed and monitored (
/health,/ready,/live) - [ ] Prometheus metrics exported (
/metricsendpoint) - [ ] Grafana dashboards configured (if using Prometheus)
Verification Command:
# Check health endpoints
curl https://your-domain.com/health
curl https://your-domain.com/ready
curl https://your-domain.com/live
# All should return 200 OK with health status
✅ Security Headers¶
- [ ] HSTS header enabled (verify with browser dev tools or curl)
- [ ] Content-Security-Policy header present
- [ ] X-Frame-Options set to
SAMEORIGIN - [ ] X-Content-Type-Options set to
nosniff - [ ] X-XSS-Protection set to
1; mode=block - [ ] Referrer-Policy set to
strict-origin-when-cross-origin - [ ] Server header removed (not advertising ASP.NET Core version)
- [ ] X-Powered-By header removed
Verification Command:
# Check security headers
curl -I https://your-domain.com | grep -i "strict-transport\|content-security\|x-frame\|x-content-type\|referrer-policy"
# Expected output:
# strict-transport-security: max-age=31536000; includeSubDomains; preload
# content-security-policy: default-src 'self'; ...
# x-frame-options: SAMEORIGIN
# x-content-type-options: nosniff
# referrer-policy: strict-origin-when-cross-origin
✅ Container Security (Docker/Kubernetes)¶
- [ ] Non-root user configured in Dockerfile (
USER cesivi) - [ ] Read-only root filesystem where possible
- [ ] Security context configured (drop all capabilities, add only NET_BIND_SERVICE)
- [ ] Resource limits configured (CPU: 2, Memory: 2Gi)
- [ ] Health checks configured (liveness, readiness probes)
- [ ] Kubernetes secrets used for sensitive data (not ConfigMaps)
- [ ] Network policies configured (restrict ingress/egress)
- [ ] Pod Security Standards enforced (Restricted or Baseline)
- [ ] Vulnerability scanning passed (Trivy, Clair, Snyk)
- [ ] Image pulled from trusted registry with image signature verification
Verification Commands:
# Docker: Check user in running container
docker exec <container-id> whoami
# Should return: cesivi (not root)
# Kubernetes: Check pod security context
kubectl get pod <pod-name> -o jsonpath='{.spec.securityContext}' --namespace=cesivi
# Scan Docker image for vulnerabilities
trivy image cesivi:1.0
✅ Testing¶
- [ ] All unit tests passing (server tests: 293/293)
- [ ] All integration tests passing (REST/SOAP tests: 383/403 or better)
- [ ] Security test suite run and passing
- [ ] Vulnerability scan completed (
dotnet list package --vulnerable) - [ ] No critical or high vulnerabilities found
- [ ] Rate limiting tested (verified 429 responses after limit exceeded)
- [ ] Authentication tested (verified access denied without valid credentials)
- [ ] Authorization tested (verified users cannot access unauthorized resources)
- [ ] HTTPS tested (verified TLS certificate valid and accepted by browsers)
- [ ] Load testing completed (verified server handles expected traffic)
Verification Commands:
# Run tests
dotnet test --verbosity quiet
# Check for vulnerable packages
dotnet list package --vulnerable
# Should output: No vulnerable packages found
✅ Compliance & Documentation¶
- [ ] SECURITY.md updated with vulnerability reporting contact
- [ ] Security best practices documented (SECURITY_BEST_PRACTICES.md)
- [ ] Deployment documentation updated with production settings
- [ ] Runbooks created for common security incidents
- [ ] Incident response plan documented
- [ ] Data retention policy documented
- [ ] Privacy policy updated (if handling user data)
- [ ] Terms of service updated (if applicable)
Environment-Specific Checklists¶
Development Environment¶
Allowed (for convenience):
- ✅ AcceptAllCredentials: true
- ✅ AllowAnonymous: true
- ✅ EnableBasic: true
- ✅ Self-signed certificates
- ✅ InMemory distributed state provider
- ✅ Verbose logging (Debug level)
Still Required: - ⚠️ Secrets in environment variables (good habit) - ⚠️ Regular package updates - ⚠️ HTTPS enabled (even with self-signed cert)
Staging Environment¶
Should mirror production:
- ⛔ AcceptAllCredentials: false
- ⛔ AllowAnonymous: false
- ⛔ EnableBasic: false
- ✅ Valid TLS certificate (or staging cert)
- ✅ Redis distributed state provider
- ✅ Production-level logging (Warning/Information)
- ✅ All production security features enabled
Purpose: Test production configuration before deploying to production
Production Environment¶
All checklist items above MUST be completed
Additional requirements: - ✅ Change management process followed - ✅ Rollback plan documented and tested - ✅ Stakeholders notified of deployment - ✅ Maintenance window scheduled (if needed) - ✅ Monitoring dashboards checked before/after deployment - ✅ Post-deployment smoke tests completed
Common Pitfalls to Avoid¶
❌ Don't:¶
- Copy development appsettings.json to production (contains insecure defaults)
- Commit .env files to git (contains production secrets)
- Use default FormDigest secret (easy to guess)
- Disable HTTPS "temporarily" (becomes permanent)
- Skip testing after configuration changes (catch issues early)
- Use InMemory provider in multi-server production (session loss on pod restart)
- Ignore vulnerability scan warnings (small issues become big problems)
- Forget to rotate secrets (compromised secrets stay compromised)
✅ Do:¶
- Use appsettings.Production.json (hardened defaults)
- Store secrets in environment variables or vault (never in git)
- Generate strong random secrets (64+ characters)
- Enforce HTTPS at reverse proxy level (defense in depth)
- Test in staging before production (catch configuration errors)
- Use Redis for production (stateless pods, better scalability)
- Update packages regularly (monthly security updates)
- Implement secret rotation (every 90 days minimum)
Sign-Off¶
Before deploying to production, confirm:
Deployment Date: ___
Deployed By: ___
Reviewed By: ___
Checklist Completed: - [ ] All items above checked and verified - [ ] Known issues documented (if any) - [ ] Rollback plan tested - [ ] Monitoring confirmed operational
Signature: ___
Post-Deployment Verification¶
After deploying to production, verify:
- [ ] Server started successfully (check logs)
- [ ] Health endpoints responding (
/health,/ready,/live) - [ ] Authentication working (test with valid and invalid credentials)
- [ ] Rate limiting working (test by exceeding limits)
- [ ] Security headers present (check with curl or browser)
- [ ] Metrics being collected (check Prometheus/Grafana)
- [ ] Logs being aggregated (check ELK/Splunk/CloudWatch)
- [ ] No errors in logs (check for startup errors)
- [ ] Sample API call succeeds (verify functionality)
- [ ] No secrets in logs (check log output)
Emergency Rollback¶
If security issues are found after deployment:
- Immediately rollback to previous version
- Document the issue in incident report
- Fix the security issue in development/staging
- Re-test thoroughly before redeploying
- Post-mortem (why did checklist miss this?)
Additional Resources¶
- SECURITY.md - Vulnerability reporting
- SECURITY_BEST_PRACTICES.md - Detailed security guide
- Security Audit Report - Internal findings
- OWASP Deployment Guidelines
Last updated: 2026-01-18 Version: 1.0 Plan: PLAN-148 Phase 2.3
Changelog¶
- 2026-01-18: Initial version (PLAN-148 Phase 2.3)