Security Guide
This guide covers security best practices for deploying and operating Milvaion in production environments. All security configurations are managed through appsettings.json, environment variables, and infrastructure settings.
Authentication Configuration
JWT Token Settings
Configure token security in appsettings.json:
{
"Milvasoft": {
"Identity": {
"Token": {
"UseUtcForDateTimes": true,
"ExpirationMinute": 90,
"TokenValidationParameters": {
"ValidateIssuer": true,
"ValidateAudience": true,
"ValidIssuer": "https://your-domain.com",
"ValidAudience": "milvaion-clients"
},
"SecurityKeyType": 0,
"SymmetricPublicKey": "your-256-bit-secret-key-here"
}
}
}
}
Recommended Settings
| Setting | Development | Production |
|---|---|---|
ExpirationMinute | 90 | 15-30 |
ValidateIssuer | false | true |
ValidateAudience | false | true |
SymmetricPublicKey | Any | Random 256-bit key |
Generate a Secure Key
MilvaIdentity requires a 256-bit (32 byte) symmetric key for JWT signing.
The key may be provided in hexadecimal or Base64 format as long as it represents 32 cryptographically secure bytes.
Windows (PowerShell – Hex Format)
# Generates a 256-bit (32 byte) AES-compatible key in HEX format
$bytes = New-Object byte[] 32
[Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
($bytes | ForEach-Object { $_.ToString("x2") }) -join ""
Example output:
dc778fe1bf2572e91cba54a9835fb2268ad7fdc8bfc2e313d23f975b09111ae3
Linux / macOS (Hex Format)
openssl rand -hex 32
Optional: Base64 Format
# Windows
[Convert]::ToBase64String(
(1..32 | ForEach-Object { Get-Random -Maximum 256 })
)
# Linux / macOS
openssl rand -base64 32
- Never reuse keys between environments
- Never commit production keys to source control
- Store keys in environment variables or secret managers
- Rotate JWT signing keys periodically (recommended: every 90 days)
Token Expiration Behavior
Milvaion handles token states with specific HTTP status codes:
| Status Code | Meaning | Client Action |
|---|---|---|
| 401 | Invalid or missing token | Redirect to login |
| 419 | Token expired | Refresh token or re-login |
| 403 | Insufficient permissions | Show access denied |
Password Security
Password Policy Configuration
Configure password requirements in appsettings.json:
{
"Milvasoft": {
"Identity": {
"Password": {
"Hasher": {
"IterationCount": 10000
},
"RequiredLength": 8,
"RequiredUniqueChars": 2,
"RequireNonAlphanumeric": true,
"RequireLowercase": true,
"RequireUppercase": true,
"RequireDigit": true
}
}
}
}
Recommended Values
| Setting | Minimum | High Security |
|---|---|---|
RequiredLength | 8 | 12+ |
IterationCount | 10000 | 100000+ |
RequiredUniqueChars | 2 | 4 |
Account Lockout Protection
Protect against brute-force attacks:
{
"Milvasoft": {
"Identity": {
"Lockout": {
"AllowedForNewUsers": true,
"MaxFailedAccessAttempts": 5,
"DefaultLockoutTimeSpan": 15
}
}
}
}
Behavior:
- After 5 failed attempts → Account locked for 15 minutes
- Lockout duration shown to user
- Successful login resets the counter
🌐 Cross-Origin Resource Sharing (CORS)
Milvaion uses a configuration-driven CORS model.
All CORS policies are defined in appsettings.json and applied dynamically at startup.
Misconfigured CORS is a common source of security vulnerabilities. Always restrict origins in production environments.
CORS Configuration
Example appsettings.json
{
"Cors": {
"DefaultPolicy": "Public",
"Policies": {
"Public": {
"Origins": [
"https://app.your-domain.com"
],
"Methods": [ "GET", "POST", "PUT", "PATCH", "DELETE" ],
"Headers": [ "Content-Type", "Authorization" ],
"AllowCredentials": false
}
}
}
}
Internal / Trusted Environments (Allow All with Credentials)
For internal APIs, admin panels, or gateway-protected services, Milvaion supports a
controlled “allow all origins with credentials” mode using SetIsOriginAllowed.
{
"Cors": {
"DefaultPolicy": "AllowAll",
"Policies": {
"AllowAll": {
"AllowAnyOriginWithCredentials": true,
"Origins": [ "All" ],
"Methods": [ "All" ],
"Headers": [ "All" ],
"ExposedHeaders": [ "Content-Disposition" ],
"AllowCredentials": true
}
}
}
}
- This configuration MUST NOT be exposed to the public internet.
- Use it only behind a reverse proxy, VPN, or API gateway.
Environment-Based CORS Strategy
| Environment | Recommended Policy |
|---|---|
| Development | AllowAll |
| QA / Staging | Whitelisted domains |
| Production (Public API) | Strict origin whitelist |
| Production (Internal API) | Gateway-only AllowAll |
Example override:
// appsettings.Development.json
{
"Cors": {
"DefaultPolicy": "AllowAll"
}
}
// appsettings.Production.json
{
"Cors": {
"DefaultPolicy": "Public"
}
}
Default Policy Behavior
The policy defined by Cors:DefaultPolicy is automatically applied globally and default configuration value is AllowAll;
"Cors": {
"DefaultPolicy": "AllowAll",
"Policies": {
"AllowAll": {
"AllowAnyOriginWithCredentials": true,
"Origins": [ "All" ],
"Methods": [ "All" ],
"Headers": [ "All" ],
"ExposedHeaders": [ "Content-Disposition" ],
"AllowCredentials": true
}
}
}
but fully configuration-driven.
Infrastructure Security
PostgreSQL
Secure Connection String
{
"ConnectionStrings": {
"DefaultConnectionString": "Host=postgres;Port=5432;Database=MilvaionDb;Username=milvaion_app;Password=SECURE_PASSWORD;SSL Mode=Require;Trust Server Certificate=false;Pooling=true;Maximum Pool Size=100"
}
}
Best Practices
| Practice | Implementation |
|---|---|
| Dedicated user | Create milvaion_app with minimum required permissions |
| SSL/TLS | Enable SSL Mode=Require |
| Network isolation | Use private network, no public IP |
| Connection limits | Set Maximum Pool Size appropriately |
Database User Permissions
-- Create application user with minimal permissions
CREATE USER milvaion_app WITH PASSWORD 'secure_password';
-- Grant only necessary permissions
GRANT CONNECT ON DATABASE MilvaionDb TO milvaion_app;
GRANT USAGE ON SCHEMA public TO milvaion_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO milvaion_app;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO milvaion_app;
-- For migrations (use a separate admin user)
CREATE USER milvaion_admin WITH PASSWORD 'admin_password';
GRANT CREATE ON SCHEMA public TO milvaion_admin;
Redis
API Configuration
{
"MilvaionConfig": {
"Redis": {
"ConnectionString": "redis:6379,ssl=true",
"Password": "STRONG_REDIS_PASSWORD",
"Database": 0,
"ConnectTimeout": 5000,
"SyncTimeout": 5000
}
}
}
Worker Configuration
{
"Worker": {
"Redis": {
"ConnectionString": "redis:6379",
"Password": "SAME_REDIS_PASSWORD",
"Database": 0,
"CancellationChannel": "Milvaion:JobScheduler:cancellation_channel"
}
}
}
Redis Server Hardening
# redis.conf
requirepass YOUR_STRONG_PASSWORD
bind 127.0.0.1 # Or private network IP only
protected-mode yes
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command DEBUG ""