This document outlines the security features implemented in Unuxt.
All environment variables are validated at server startup using Zod schemas. The application will fail fast with clear error messages if required variables are missing or invalid.
Location: packages/config/src/env.ts
- ✅ Type-safe environment variables with TypeScript inference
- ✅ Automatic validation on server startup
- ✅ Clear, formatted error messages for missing/invalid variables
- ✅ Feature flags to check if optional features are configured
- ✅ Comprehensive test coverage
DATABASE_URL- PostgreSQL connection string (validated format)BETTER_AUTH_SECRET- Must be at least 32 charactersBETTER_AUTH_URL- Valid URL for the application
import { featureFlags } from '@unuxt/config/env'
// Check if optional features are configured
if (featureFlags.hasGoogleOAuth()) {
// Google OAuth is configured
}
if (featureFlags.hasEmailConfig()) {
// SMTP is configured, send emails
}
if (featureFlags.isProduction()) {
// Running in production mode
}The application uses the nuxt-security module to apply comprehensive security headers.
Configuration: apps/web/nuxt.config.ts
- Prevents XSS attacks with strict script/style policies
- Allows Cloudinary images for user avatars and org logos
- Nonce-based script execution
upgrade-insecure-requestsenabled
- Enabled for POST, PUT, PATCH, DELETE requests
- Excludes
/api/auth/**(Better Auth has its own CSRF protection) - Automatic token validation
- Global: 150 requests per minute per IP
- Auth endpoints: 5 failed attempts = 15 minute cooldown (Better Auth)
- Configurable in
nuxt.config.ts
Strict-Transport-Security- HSTS with 180-day max-ageX-Frame-Options: SAMEORIGIN- Prevents clickjackingX-Content-Type-Options: nosniff- Prevents MIME sniffingX-XSS-Protection: 1; mode=block- XSS filterReferrer-Policy: no-referrer- No referrer informationPermissions-Policy- Restricts camera, geolocation, microphoneCross-Origin-*headers - Prevents cross-origin attacks
- Same-origin policy by default
- Configurable via
nuxt.config.ts
Enhanced password validation with complexity requirements and breach checking.
Location: packages/utils/src/validation.ts, packages/utils/src/security.ts
All passwords must meet these requirements:
- ✅ Minimum 8 characters
- ✅ At least one uppercase letter (A-Z)
- ✅ At least one lowercase letter (a-z)
- ✅ At least one number (0-9)
- ✅ At least one special character (!@#$%^&*()_+-=[]{}|;:,.<>?)
import { checkPasswordStrength } from '@unuxt/utils'
const result = checkPasswordStrength('MyP@ssw0rd123')
// {
// strength: 'strong', // 'weak' | 'fair' | 'good' | 'strong'
// score: 5, // 0-4
// feedback: [], // Array of improvement suggestions
// isValid: true // Meets all requirements
// }Passwords are checked against the Have I Been Pwned (HIBP) database to prevent the use of compromised passwords.
Features:
- ✅ k-anonymity model (only sends first 5 characters of SHA-1 hash)
- ✅ Automatic caching (1 hour TTL) to prevent duplicate API calls
- ✅ Graceful fallback on API errors
- ✅ Enabled in production, optional in development
Usage:
import { checkPasswordBreach, validatePassword } from '@unuxt/utils'
// Check for breaches only
const breachResult = await checkPasswordBreach('password123')
if (breachResult.isBreached) {
console.log(`Found in ${breachResult.breachCount} breaches!`)
}
// Full validation with breach check
const validation = await validatePassword('MyP@ssw0rd123', true)
if (!validation.isValid) {
console.error(validation.errors)
}
if (validation.isBreached) {
console.error('Password has been compromised!')
}Server-side helper:
// In API routes
import { validatePasswordWithBreachCheck } from '~/server/utils/password-validation'
export default defineEventHandler(async (event) => {
const { password } = await readBody(event)
const validation = await validatePasswordWithBreachCheck(password)
if (!validation.isValid) {
throw createError({
statusCode: 400,
message: validation.errors.join(', ')
})
}
if (validation.isBreached) {
throw createError({
statusCode: 400,
message: 'This password has been exposed in data breaches'
})
}
// Continue with registration/password reset...
})- ✅ All user input validated on server-side
- ✅ Zod schemas for type-safe validation
- ✅ SQL injection prevention via Drizzle ORM parameterized queries
- ✅ XSS prevention via CSP headers and Vue's automatic escaping
- ✅ Better Auth with secure session management
- ✅ CSRF protection on auth endpoints
- ✅ Rate limiting on login attempts
- ✅ 2FA support with TOTP
- ✅ Magic link authentication
- ✅ Secure password reset flow
- ✅ Role-based access control (RBAC)
- ✅ Organization-based permissions
- ✅ Middleware for route protection
- ✅ API route authentication checks
- ✅ HTTPS enforced in production
- ✅ Secure session cookies (httpOnly, secure, sameSite)
- ✅ Environment variables never exposed to client
- ✅ Database credentials stored securely
- ✅ Environment validation on startup
- ✅ Clear error messages for security issues
- ✅ Feature flags for optional security features
If you need to add third-party scripts or styles, update the CSP in apps/web/nuxt.config.ts:
security: {
headers: {
contentSecurityPolicy: {
'script-src': [
"'self'",
'https://trusted-cdn.com', // Add trusted sources
// ...
],
},
},
}Update rate limiting in apps/web/nuxt.config.ts:
security: {
rateLimiter: {
tokensPerInterval: 200, // Increase to 200 requests
interval: 60000, // per minute
},
}To disable specific security features (not recommended for production):
security: {
csrf: {
enabled: false, // Disable CSRF (NOT RECOMMENDED)
},
}Before deploying to production, ensure:
-
BETTER_AUTH_SECRETis at least 32 characters and unique -
DATABASE_URLuses SSL connection (?sslmode=require) - All required environment variables are set
- SMTP is configured for email notifications
- HTTPS is enforced (Let's Encrypt, Cloudflare, etc.)
- Database backups are configured
- Rate limiting is appropriate for your use case
- Security headers are enabled (default)
- Password breach checking is enabled (default in production)
- CSP is configured correctly for your third-party integrations
If you discover a security vulnerability, please email security@yourapp.com (replace with your security contact).
Do NOT create a public GitHub issue for security vulnerabilities.