Security
Security features and best practices for safe-env.
Secret Protection
All values marked with the secret modifier are fully masked in error messages.
How It Works
Instead of showing partial values (like abcd****xyz), safe-env shows only the length:
env({
JWT_SECRET: "string:min=32:secret",
});
// Error message:
// JWT_SECRET: String must contain at least 32 character(s)
// (received: [REDACTED (length: 12)])
// ✅ No partial values exposed
// ✅ No information leakage
// ✅ Only length shown for debuggingBest Practice
Always mark sensitive values as secret:
env({
// ✅ Good: Marked as secret
JWT_SECRET: "string:min=32:secret",
API_KEY: "string:min=20:secret",
DATABASE_URL: "string:url:secret", // URLs can be sensitive too
// ⚠️ Warning: Not marked as secret
// Full value will be shown in errors if validation fails
LOG_LEVEL: "string:default=info",
});DoS Protection
Built-in limits prevent resource exhaustion attacks:
Security Limits
- DSL strings: Max 1000 characters
- Environment variables: Max 1000 per schema
- Variable keys: Max 200 characters
- Enum values: Max 100 values, 200 chars each
- Modifiers: Max 20 per DSL string
- Default values: Max 500 characters
// These will throw errors:
env({
// ❌ Too many variables
...Object.fromEntries(
Array.from({ length: 1001 }, (_, i) => [`VAR_${i}`, "string"])
),
});
env({
// ❌ DSL string too long
VAR: "string:" + "x".repeat(1001),
});
env({
// ❌ Too many enum values
MODE: "enum:" + Array.from({ length: 101 }, (_, i) => `val${i}`).join(","),
});Security Best Practices
1. Mark All Secrets
// ✅ Good
env({
JWT_SECRET: "string:min=32:secret",
API_KEY: "string:min=20:secret",
DATABASE_URL: "string:url:secret",
PRIVATE_KEY: "string:secret",
});
// ❌ Bad: Secrets not marked
env({
JWT_SECRET: "string:min=32", // Will expose in errors!
API_KEY: "string:min=20", // Will expose in errors!
});2. Don't Log Environment Variables
// ❌ Bad: Logging secrets
console.log("API Key:", ENV.API_KEY);
console.log("JWT Secret:", ENV.JWT_SECRET);
logger.info({ apiKey: ENV.API_KEY }); // Goes to logs!
// ✅ Good: Only log non-sensitive info
console.log("API Key configured:", !!ENV.API_KEY);
console.log("API Key length:", ENV.API_KEY.length);
console.log("Environment:", ENV.NODE_ENV);
logger.info({ hasApiKey: !!ENV.API_KEY });3. Validate Early
// ✅ Good: Validate at startup
// config/env.ts
import { env } from "@liahus/safe-env";
export const ENV = env({
DATABASE_URL: "string:url",
PORT: "number:default=3000",
});
// Application fails fast if validation fails
// server.ts
import { ENV } from "./config/env";
// ... rest of app4. Use Strong Defaults
// ✅ Good: Sensible defaults
env({
PORT: "number:default=3000",
LOG_LEVEL: "string:default=info",
TIMEOUT: "number:min=1000:default=5000",
});
// ⚠️ Avoid defaults for secrets
// Force explicit configuration
env({
JWT_SECRET: "string:min=32:secret", // No default - must be provided
API_KEY: "string:min=20:secret", // No default - must be provided
});Error Message Security
What's Exposed
- Non-secret values: Full value shown (for debugging)
- Secret values: Only length shown
[REDACTED (length: X)] - Error messages: Clean, no stack traces
import { env, EnvValidationError } from "@liahus/safe-env";
try {
const ENV = env({
DATABASE_URL: "string:url",
PORT: "number",
JWT_SECRET: "string:min=32:secret",
});
} catch (error) {
if (error instanceof EnvValidationError) {
console.error(error.message);
// Output:
// Environment variable validation failed:
// DATABASE_URL: Invalid url (received: undefined)
// PORT: Required (received: undefined)
// JWT_SECRET: String must contain at least 32 character(s)
// (received: [REDACTED (length: 12)])
// ✅ DATABASE_URL: Full value shown (not sensitive)
// ✅ PORT: Full value shown (not sensitive)
// ✅ JWT_SECRET: Only length shown (secret protected)
}
}Security Considerations
✅ What safe-env Does
- • Masks secrets in error messages
- • Validates input to prevent DoS
- • Provides clean error messages
- • No filesystem access
- • No network access
- • No external API calls
⚠️ What You Need to Do
- • Mark sensitive values as
secret - • Don't log environment variables
- • Use secure storage for secrets (not in code)
- • Validate at application startup
- • Use strong, unique secrets