Files
Sankofa/api/src/middleware/security.ts
defiQUG 9daf1fd378 Apply Composer changes: comprehensive API updates, migrations, middleware, and infrastructure improvements
- Add comprehensive database migrations (001-024) for schema evolution
- Enhance API schema with expanded type definitions and resolvers
- Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth
- Implement new services: AI optimization, billing, blockchain, compliance, marketplace
- Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage)
- Update Crossplane provider with enhanced VM management capabilities
- Add comprehensive test suite for API endpoints and services
- Update frontend components with improved GraphQL subscriptions and real-time updates
- Enhance security configurations and headers (CSP, CORS, etc.)
- Update documentation and configuration files
- Add new CI/CD workflows and validation scripts
- Implement design system improvements and UI enhancements
2025-12-12 18:01:35 -08:00

142 lines
4.5 KiB
TypeScript

/**
* Security Middleware
* Implements security headers and protections per DoD/MilSpec standards
*
* Complies with:
* - DISA STIG: Web Server Security
* - NIST SP 800-53: SI-4 (Information System Monitoring)
* - NIST SP 800-171: 3.13.1 (Cryptographic Protection in Transit)
*/
import { FastifyRequest, FastifyReply } from 'fastify'
import { randomBytes } from 'crypto'
/**
* Add security headers to responses per DoD/MilSpec requirements
*
* Implements comprehensive security headers as required by:
* - DISA STIG for Web Servers
* - OWASP Secure Headers Project
* - DoD Security Technical Implementation Guides
*/
export async function securityHeadersMiddleware(
request: FastifyRequest,
reply: FastifyReply
): Promise<void> {
// Prevent MIME type sniffing (DISA STIG requirement)
reply.header('X-Content-Type-Options', 'nosniff')
// Prevent clickjacking attacks (DISA STIG requirement)
reply.header('X-Frame-Options', 'DENY')
// Legacy XSS protection (deprecated but still recommended for older browsers)
reply.header('X-XSS-Protection', '1; mode=block')
// HTTP Strict Transport Security (HSTS) with preload
// max-age: 1 year (31536000 seconds)
// includeSubDomains: Apply to all subdomains
// preload: Allow inclusion in HSTS preload lists
const hstsMaxAge = 31536000 // 1 year
reply.header('Strict-Transport-Security', `max-age=${hstsMaxAge}; includeSubDomains; preload`)
// Content Security Policy (CSP) per STIG requirements
// Strict CSP to prevent XSS and injection attacks
// Generate nonce for inline scripts/styles to avoid unsafe-inline
const nonce = randomBytes(16).toString('base64')
// Store nonce in request for use in templates
;(request as any).cspNonce = nonce
const csp = [
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}'`, // Use nonce instead of unsafe-inline
`style-src 'self' 'nonce-${nonce}'`, // Use nonce instead of unsafe-inline
"img-src 'self' data: https:",
"font-src 'self' data:",
"connect-src 'self'",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
"upgrade-insecure-requests",
].join('; ')
reply.header('Content-Security-Policy', csp)
// Referrer Policy - control referrer information leakage
reply.header('Referrer-Policy', 'strict-origin-when-cross-origin')
// Permissions Policy (formerly Feature Policy) - disable unnecessary features
reply.header('Permissions-Policy', [
'geolocation=()',
'microphone=()',
'camera=()',
'payment=()',
'usb=()',
'magnetometer=()',
'gyroscope=()',
'accelerometer=()',
].join(', '))
// X-Permitted-Cross-Domain-Policies - restrict cross-domain policies
reply.header('X-Permitted-Cross-Domain-Policies', 'none')
// Expect-CT - Certificate Transparency (deprecated but still used)
// Note: This header is deprecated but may still be required for some compliance
// reply.header('Expect-CT', 'max-age=86400, enforce')
// Cross-Origin-Embedder-Policy - prevent cross-origin data leakage
reply.header('Cross-Origin-Embedder-Policy', 'require-corp')
// Cross-Origin-Opener-Policy - isolate browsing context
reply.header('Cross-Origin-Opener-Policy', 'same-origin')
// Cross-Origin-Resource-Policy - control resource loading
reply.header('Cross-Origin-Resource-Policy', 'same-origin')
// Remove server information disclosure
// Note: Fastify doesn't expose server header by default, but we ensure it's not set
reply.removeHeader('Server')
reply.removeHeader('X-Powered-By')
}
/**
* Input sanitization helper
*/
export function sanitizeInput(input: unknown): unknown {
if (typeof input === 'string') {
// Remove potentially dangerous characters
return input
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
.replace(/javascript:/gi, '')
.replace(/on\w+\s*=/gi, '')
.trim()
}
if (Array.isArray(input)) {
return input.map(sanitizeInput)
}
if (input && typeof input === 'object' && !Array.isArray(input)) {
const sanitized: Record<string, unknown> = {}
for (const key in input) {
if (Object.prototype.hasOwnProperty.call(input, key)) {
sanitized[key] = sanitizeInput((input as Record<string, unknown>)[key])
}
}
return sanitized
}
return input
}
/**
* Validate and sanitize request body
*/
export async function sanitizeBodyMiddleware(
request: FastifyRequest,
reply: FastifyReply
): Promise<void> {
if (request.body) {
request.body = sanitizeInput(request.body)
}
}