security config
- Source:
src/config/security.ts
Usage
Import from the framework:
ts
import { security } from '@zintrust/core';
// Example (if supported by the module):
// security.*Encrypted envelope interoperability
ZinTrust supports reading/writing framework-compatible encrypted payloads (e.g. encrypted DB columns) using the same key material that other ecosystems commonly use.
Required env vars:
ENCRYPTION_CIPHER: Cipher used for encrypted envelopes. Supported values (case-insensitive):aes-256-cbcaes-256-gcm
APP_KEY: Base64 key material (32 bytes). Supports bothbase64:...and raw base64.
Optional:
APP_PREVIOUS_KEYS: Key rotation fallback (comma-separated keys or JSON array). During decryption, ZinTrust will tryAPP_KEYfirst, then each previous key.
Migration guidance:
- Set
ENCRYPTION_CIPHERto match your previous framework so you can decrypt existing DB values without re-encrypting. - Keep
APP_KEYthe same value you already use in production.
Examples
Encrypt/decrypt strings (encrypted envelope)
ts
import { EncryptedEnvelope } from '@zintrust/core';
const cipher = 'aes-256-cbc';
const key = process.env.APP_KEY!; // supports `base64:...` or raw base64
const encrypted = EncryptedEnvelope.encryptString('hello', { cipher, key });
const plain = EncryptedEnvelope.decryptString(encrypted, { cipher, key });Decrypt with key rotation (APP_PREVIOUS_KEYS)
ts
import { EncryptedEnvelope } from '@zintrust/core';
const cipher = process.env.ENCRYPTION_CIPHER!;
const key = process.env.APP_KEY!;
// Example: if you rotated keys, keep the old ones here (comma-separated or JSON array)
const previousKeys = (process.env.APP_PREVIOUS_KEYS ?? '')
.split(',')
.map((s) => s.trim())
.filter(Boolean);
const plain = EncryptedEnvelope.decryptString('<db_value>', { cipher, key, previousKeys });Encrypt/decrypt structured data (serialized payload envelope)
ZinTrust does not assume a specific serialization format. You provide it:
ts
import { EncryptedEnvelope } from '@zintrust/core';
const serializer = {
serialize: (v: unknown) => JSON.stringify(v),
deserialize: (s: string) => JSON.parse(s) as unknown,
};
const cipher = process.env.ENCRYPTION_CIPHER!;
const key = process.env.APP_KEY!;
const encrypted = EncryptedEnvelope.encrypt({ a: 1 }, { cipher, key, serializer });
const decrypted = EncryptedEnvelope.decrypt(encrypted, { cipher, key, serializer });Snapshot (top)
ts
/**
* Security Configuration
* JWT, CSRF, encryption and other security settings
* Sealed namespace for immutability
*
* APP_KEY: Primary encryption key for storage signing and app-level encryption.
* Set automatically during project scaffolding.
*
* Security keys can be configured per domain:
* - APP_KEY: Default encryption key for all operations (auto-generated)
* - API_KEY_SECRET: Optional API key authentication (if API_KEY_ENABLED=true)
* - ENCRYPTION_KEY: Optional separate encryption key (overrides APP_KEY if set)
* - JWT_SECRET: JWT token signing key
*
* Developers can use a single APP_KEY or configure separate keys for different
* security domains (e.g., different keys for different microservices).
*/
import { Env } from '@zintrust/core';
import { appConfig, ErrorFactory, Logger } from '@zintrust/core';
/**
* Helper to warn about missing secrets
*/
function warnMissingSecret(secretName: string): string {
Logger.error(`❌ CRITICAL: ${secretName} environment variable is not set!`);
Logger.error('⚠️ Application may not function correctly. Set this in production immediately.');
if (appConfig.isProduction()) {
throw ErrorFactory.createConfigError(`Missing required secret: ${secretName}`, { secretName });
}
// In non-production environments, allow the app/CLI to start while still warning loudly.
// This is intentionally predictable for local development and test tooling.
return 'dev-unsafe-jwt-secret';
}
let cachedJwtSecret: string | undefined;
const securityConfigObj = {
/**
* JWT Configuration
*/
jwt: {
enabled: Env.getBool('JWT_ENABLED', true),
get secret(): string {
if (cachedJwtSecret !== undefined) return cachedJwtSecret;
const isEnabled = Env.getBool('JWT_ENABLED', true);
cachedJwtSecret = isEnabled
? Env.get('JWT_SECRET') || warnMissingSecret('JWT_SECRET')
: Env.get('JWT_SECRET') || '';
return cachedJwtSecret;
},
algorithm: Env.get('JWT_ALGORITHM', 'HS256') as 'HS256' | 'HS512' | 'RS256',
expiresIn: Env.get('JWT_EXPIRES_IN', '1h'),
refreshExpiresIn: Env.get('JWT_REFRESH_EXPIRES_IN', '7d'),
issuer: Env.get('JWT_ISSUER', 'zintrust'),
audience: Env.get('JWT_AUDIENCE', 'zintrust-api'),
},
/**
* CSRF Protection
*/
csrf: {
enabled: Env.getBool('CSRF_ENABLED', true),
headerName: Env.get('CSRF_HEADER_NAME', 'x-csrf-token'),
tokenName: Env.get('CSRF_TOKEN_NAME', '_csrf'),
cookieName: Env.get('CSRF_COOKIE_NAME', 'XSRF-TOKEN'),
cookieHttpOnly: Env.getBool('CSRF_COOKIE_HTTP_ONLY', true),Snapshot (bottom)
ts
maxAge: Env.getInt('CORS_MAX_AGE', 86400),
},
/**
* Rate Limiting
*/
rateLimit: {
enabled: Env.getBool('RATE_LIMIT_ENABLED', true),
windowMs: Env.getInt('RATE_LIMIT_WINDOW_MS', 900000), // 15 minutes
maxRequests: Env.getInt('RATE_LIMIT_MAX_REQUESTS', 100),
message: Env.get('RATE_LIMIT_MESSAGE', 'Too many requests, please try again later'),
},
/**
* XSS Protection
*/
xss: {
enabled: Env.getBool('XSS_ENABLED', true),
reportUri: Env.get('XSS_REPORT_URI'),
},
/**
* Helmet Security Headers
*/
helmet: {
enabled: Env.getBool('HELMET_ENABLED', true),
contentSecurityPolicy: Env.getBool('CSP_ENABLED', true),
hsts: {
enabled: Env.getBool('HSTS_ENABLED', true),
maxAge: Env.getInt('HSTS_MAX_AGE', 31536000),
includeSubDomains: Env.getBool('HSTS_INCLUDE_SUBDOMAINS', true),
},
},
/**
* Session Configuration
*/
session: {
name: Env.get('SESSION_NAME', 'zintrust_session'),
secret: Env.get('SESSION_SECRET', 'your-session-secret'),
expiresIn: Env.getInt('SESSION_EXPIRES_IN', 1800000), // 30 minutes
secure: Env.getBool('SESSION_SECURE', true),
httpOnly: Env.getBool('SESSION_HTTP_ONLY', true),
sameSite: Env.get('SESSION_SAME_SITE', 'strict') as 'strict' | 'lax' | 'none',
},
/**
* Password settings
*/
password: {
minLength: Env.getInt('PASSWORD_MIN_LENGTH', 8),
requireUppercase: Env.getBool('PASSWORD_REQUIRE_UPPERCASE', true),
requireNumbers: Env.getBool('PASSWORD_REQUIRE_NUMBERS', true),
requireSpecialChars: Env.getBool('PASSWORD_REQUIRE_SPECIAL_CHARS', true),
bcryptRounds: Env.getInt('BCRYPT_ROUNDS', 10),
},
} as const;
export const securityConfig = Object.freeze(securityConfigObj);