Security Best Practices Guide¶
Cesivi Server - Production Deployment Security Version: 1.0 Last Updated: 2026-01-18 Plan: PLAN-148 Phase 2.3
Table of Contents¶
- Authentication Configuration
- Network Security
- Storage Encryption
- Secrets Management
- Docker Deployment Security
- Kubernetes Deployment Security
- Regular Updates
- Monitoring & Logging
- Security Checklist
Authentication Configuration¶
Development vs. Production¶
| Setting | Development | Production |
|---|---|---|
AcceptAllCredentials |
✅ true |
⛔ false (enforced) |
AllowAnonymous |
✅ true |
⛔ false |
EnableBasic |
✅ true |
⛔ false |
EnableNTLM |
✅ true |
✅ true |
EnableJWT |
✅ true |
✅ true |
Configuration Example (Production)¶
{
"Cesivi": {
"Authentication": {
"AcceptAllCredentials": false,
"AllowAnonymous": false,
"EnableNTLM": true,
"EnableJWT": true,
"EnableBasic": false
}
}
}
Why Disable Basic Authentication in Production?¶
Basic authentication sends username and password in Base64 encoding, which is easily decoded. Even over HTTPS, this creates unnecessary risk.
Use instead: - NTLM: Windows integrated authentication (Active Directory) - JWT: Token-based authentication (modern APIs)
AcceptAllCredentials Protection¶
Cesivi Server will throw an exception at startup if AcceptAllCredentials is enabled in Production environment:
SECURITY ERROR: AcceptAllCredentials cannot be enabled in Production environment.
Set Cesivi:Authentication:AcceptAllCredentials to false in appsettings.Production.json
This prevents accidental deployment with authentication bypass enabled.
Network Security¶
HTTPS Configuration¶
⚠️ CRITICAL: Always use HTTPS in production.
Enable HTTPS in appsettings.json¶
{
"Cesivi": {
"UseHttps": true,
"HttpPort": 80,
"HttpsPort": 443
},
"Kestrel": {
"Certificate": {
"Path": "/path/to/certificate.pfx",
"Password": "YOUR_CERTIFICATE_PASSWORD",
"AllowInvalid": false
}
}
}
TLS Version Recommendations¶
Minimum TLS version: TLS 1.2 (prefer TLS 1.3)
Configure in Kestrel:
// Program.cs (requires code modification)
builder.WebHost.ConfigureKestrel(options =>
{
options.ConfigureHttpsDefaults(httpsOptions =>
{
httpsOptions.SslProtocols = System.Security.Authentication.SslProtocols.Tls12 |
System.Security.Authentication.SslProtocols.Tls13;
});
});
Certificate Management¶
Development:
- Use self-signed certificates (generated with dotnet dev-certs https)
- Set AllowInvalid: true in Kestrel configuration
Production:
- Use certificates from a trusted Certificate Authority (Let's Encrypt, DigiCert, etc.)
- Set AllowInvalid: false
- Configure automatic certificate renewal (certbot, ACME protocol)
Reverse Proxy Recommendations¶
Use a reverse proxy (nginx, IIS, Apache) for additional security:
Benefits: - SSL/TLS termination - Rate limiting (additional layer) - DDoS protection - Load balancing - Request filtering - Security headers (additional layer)
Example nginx configuration:
server {
listen 443 ssl http2;
server_name mocksharepoint.example.com;
ssl_certificate /etc/ssl/certs/mocksharepoint.crt;
ssl_certificate_key /etc/ssl/private/mocksharepoint.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers (additional to Cesivi's built-in headers)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Rate limiting (additional to Cesivi's built-in rate limiting)
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/m;
limit_req zone=api burst=20 nodelay;
location / {
proxy_pass http://localhost:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Request size limit (matches Cesivi's limit)
client_max_body_size 100M;
}
}
Storage Encryption¶
Data at Rest¶
File System Storage: - Use encrypted file systems (BitLocker, LUKS, dm-crypt) - Configure in OS/infrastructure layer - No application-level configuration needed
SQL Server: - Enable Transparent Data Encryption (TDE) - Encrypt connection strings in storage
-- Enable TDE on SQL Server
ALTER DATABASE CesiviDB
SET ENCRYPTION ON;
PostgreSQL: - Enable pgcrypto extension for column-level encryption - Use encrypted file system for data directory
Data in Transit¶
Internal Communication: - Use HTTPS for all API calls - Use encrypted Redis connections (if using distributed state)
Redis Encryption (Production):
{
"Cesivi": {
"DistributedState": {
"Provider": "Redis",
"ConnectionString": "redis.example.com:6379,password=STRONG_PASSWORD,ssl=true,abortConnect=false"
}
}
}
Note: ssl=true enables TLS for Redis connections
Secrets Management¶
⛔ NEVER Store Secrets in appsettings.json¶
Bad Practice:
{
"Cesivi": {
"DistributedState": {
"ConnectionString": "localhost:6379,password=MyPassword123"
}
}
}
✅ Use Environment Variables¶
Good Practice:
# Set environment variables
export CESIVI_DISTRIBUTEDSTATE__CONNECTIONSTRING="redis.example.com:6379,password=STRONG_PASSWORD,ssl=true"
export CESIVI_FORMDIGEST__SHAREDSECRET="YOUR_STRONG_SECRET_AT_LEAST_32_CHARACTERS"
appsettings.Production.json:
{
"Cesivi": {
"DistributedState": {
"ConnectionString": "REPLACE_WITH_REDIS_CONNECTION_STRING"
},
"FormDigest": {
"SharedSecret": "REPLACE_WITH_STRONG_SECRET_AT_LEAST_32_CHARACTERS"
}
}
}
Secret Rotation Policy¶
Rotate secrets regularly:
| Secret | Rotation Frequency |
|---|---|
| FormDigest SharedSecret | Every 90 days |
| Database passwords | Every 90 days |
| Redis passwords | Every 90 days |
| TLS certificates | Before expiry (30 days warning) |
Docker Deployment Security¶
Secure Dockerfile¶
Best Practices:
# Use official ASP.NET Core runtime image
FROM mcr.microsoft.com/dotnet/aspnet:10.0
# Create non-root user
RUN addgroup --system --gid 1000 cesivi && \
adduser --system --uid 1000 --ingroup cesivi cesivi
# Set working directory
WORKDIR /app
# Copy published application
COPY --chown=cesivi:cesivi ./publish .
# Use non-root user
USER cesivi
# Expose port (note: use reverse proxy in production, not direct exposure)
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD curl --fail http://localhost:8080/health || exit 1
# Run application
ENTRYPOINT ["dotnet", "Cesivi.dll"]
Docker Compose Security¶
Production docker-compose.yml:
version: '3.8'
services:
cesivi:
image: cesivi:1.0
restart: unless-stopped
environment:
- ASPNETCORE_ENVIRONMENT=Production
- CESIVI_DISTRIBUTEDSTATE__CONNECTIONSTRING=${REDIS_CONNECTION_STRING}
- CESIVI_FORMDIGEST__SHAREDSECRET=${FORMDIGEST_SECRET}
volumes:
- ./data:/app/MockData:rw
networks:
- backend
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
volumes:
- redis-data:/data
networks:
- backend
security_opt:
- no-new-privileges:true
networks:
backend:
driver: bridge
volumes:
redis-data:
Environment file (.env):
# CRITICAL: Never commit .env to git!
REDIS_CONNECTION_STRING=redis:6379,password=STRONG_PASSWORD
REDIS_PASSWORD=STRONG_PASSWORD
FORMDIGEST_SECRET=YOUR_STRONG_SECRET_AT_LEAST_32_CHARACTERS
Kubernetes Deployment Security¶
Secure Deployment¶
Best Practices:
apiVersion: apps/v1
kind: Deployment
metadata:
name: cesivi-server
namespace: cesivi
spec:
replicas: 3
selector:
matchLabels:
app: cesivi-server
template:
metadata:
labels:
app: cesivi-server
spec:
# Use service account with minimal permissions
serviceAccountName: cesivi-server
# Security context for pod
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: cesivi
image: cesivi:1.0
imagePullPolicy: Always
# Security context for container
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# Resource limits
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2"
# Environment variables from secrets
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
- name: CESIVI_DISTRIBUTEDSTATE__CONNECTIONSTRING
valueFrom:
secretKeyRef:
name: cesivi-secrets
key: redis-connection-string
- name: CESIVI_FORMDIGEST__SHAREDSECRET
valueFrom:
secretKeyRef:
name: cesivi-secrets
key: formdigest-secret
# Probes
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
# Volumes for writable directories
volumeMounts:
- name: data
mountPath: /app/MockData
- name: tmp
mountPath: /tmp
volumes:
- name: data
persistentVolumeClaim:
claimName: cesivi-data
- name: tmp
emptyDir: {}
Kubernetes Secrets¶
Never commit secrets to git:
# Create secret from environment variables
kubectl create secret generic cesivi-secrets \
--from-literal=redis-connection-string='redis:6379,password=STRONG_PASSWORD,ssl=true' \
--from-literal=formdigest-secret='YOUR_STRONG_SECRET_AT_LEAST_32_CHARACTERS' \
--namespace=cesivi
# Verify secret (don't output to console in production!)
kubectl describe secret cesivi-secrets --namespace=cesivi
Network Policies¶
Restrict network access:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: cesivi-server-policy
namespace: cesivi
spec:
podSelector:
matchLabels:
app: cesivi-server
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
Regular Updates¶
Update Policy¶
Frequency:
| Component | Check Frequency | Update Frequency |
|---|---|---|
| Cesivi Server | Weekly | Monthly or when security updates available |
| .NET Runtime | Weekly | Monthly or when security updates available |
| Docker Base Images | Weekly | Monthly or when security updates available |
| NuGet Packages | Weekly | Monthly or when security updates available |
| OS Patches | Daily | Weekly (non-breaking), immediately (critical security) |
Vulnerability Scanning¶
Run regularly:
# Check for vulnerable NuGet packages
dotnet list package --vulnerable
# Update NuGet packages
dotnet restore
# Docker image scanning (example with Trivy)
trivy image cesivi:1.0
# Kubernetes scanning (example with kubesec)
kubesec scan deployment.yaml
Monitoring & Logging¶
Security Logging¶
What to log:
✅ Authentication attempts (success and failure) ✅ Authorization failures (denied access) ✅ Rate limit violations (429 responses) ✅ Failed input validation ✅ Admin actions (configuration changes, user management) ✅ Suspicious activity patterns
What NOT to log:
⛔ Passwords or credentials ⛔ Authentication tokens ⛔ Personally Identifiable Information (PII) ⛔ API keys or secrets
Log Aggregation¶
Recommended:
- ELK Stack (Elasticsearch, Logstash, Kibana)
- Splunk
- Azure Monitor
- AWS CloudWatch
Security Alerts¶
Configure alerts for:
- 10+ failed login attempts in 1 minute (potential brute-force)
- 100+ rate limit violations in 5 minutes (potential DoS)
- Unauthorized access attempts (authorization failures)
- Unexpected error spikes (potential attack)
Security Checklist¶
Use this checklist before every production deployment:
Authentication & Authorization¶
- [ ]
AcceptAllCredentialsset tofalse - [ ]
AllowAnonymousset tofalse - [ ] Basic authentication disabled (
EnableBasic: false) - [ ] Strong authentication method enabled (NTLM or JWT)
- [ ]
[Authorize]attributes present on all API controllers
Network Security¶
- [ ] HTTPS enabled with valid TLS certificate
- [ ] TLS 1.2 or 1.3 minimum enforced
- [ ] HTTP redirects to HTTPS (or HTTP disabled)
- [ ] Reverse proxy configured (nginx, IIS, or cloud load balancer)
- [ ] CORS policy configured properly (not
AllowAll)
Rate Limiting & Request Limits¶
- [ ] Rate limiting enabled (
IpRateLimiting:EnableEndpointRateLimiting: true) - [ ] Production rate limits configured (stricter than development)
- [ ] Request size limits configured (
MaxRequestBodySize,MaxCsomBatchSize) - [ ] File upload size limit appropriate (
MaxFileUploadSize)
Secrets & Configuration¶
- [ ] Secrets stored in environment variables (not appsettings.json)
- [ ]
FormDigest:SharedSecretchanged from default - [ ] Redis connection string not in appsettings.json
- [ ] Database connection strings not in appsettings.json
- [ ]
.envfile added to.gitignore
Infrastructure¶
- [ ] Redis used for distributed state (not InMemory)
- [ ] Database backups configured and tested
- [ ] Data encrypted at rest (TDE, encrypted file system)
- [ ] Data encrypted in transit (HTTPS, Redis SSL)
- [ ] Kubernetes secrets used (not ConfigMaps) for sensitive data
Monitoring & Logging¶
- [ ] Centralized logging configured (ELK, Splunk, CloudWatch)
- [ ] Security alerts configured (failed logins, rate limits, errors)
- [ ] Log rotation configured (prevent disk fill)
- [ ] No secrets logged (passwords, tokens, API keys)
- [ ] Health check endpoints exposed (
/health,/ready,/live)
Security Headers¶
- [ ] HSTS header enabled (production + HTTPS)
- [ ] Content Security Policy configured
- [ ] X-Frame-Options set to SAMEORIGIN
- [ ] X-Content-Type-Options set to nosniff
- [ ] Server header removed
Container Security (if using Docker/K8s)¶
- [ ] Non-root user configured in container
- [ ] Read-only root filesystem (where possible)
- [ ] Security context configured (drop all capabilities, add only needed)
- [ ] Resource limits configured (CPU, memory)
- [ ] Vulnerability scanning passed (Trivy, Clair, Snyk)
- [ ] Network policies configured (Kubernetes)
Testing¶
- [ ] Security test suite run and passing
- [ ] Penetration testing completed (if applicable)
- [ ] Vulnerability scan completed (dotnet list package --vulnerable)
- [ ] Rate limiting tested (verify 429 responses)
- [ ] Authentication/authorization tested (verify access control)
Additional Resources¶
- SECURITY.md - Vulnerability reporting policy
- Security Deployment Checklist - Quick pre-deployment checklist
- Security Audit Report - Internal audit findings
- OWASP Top 10 - Industry standard vulnerabilities
- CIS Docker Benchmark - Docker security guidelines
- CIS Kubernetes Benchmark - Kubernetes security guidelines
Last updated: 2026-01-18 Version: 1.0 Plan: PLAN-148 Phase 2.3