Security Features

MoroJS includes basic security features like CORS and Helmet. For advanced security features, integrate with middleware and third-party libraries.

Built-in Security Features

MoroJS includes basic security features out of the box. For advanced security, integrate with middleware libraries and implement custom security measures.

Basic Security Configuration

typescript

1import { createApp } from '@morojs/moro';
2
3// Basic security setup (actual implementation)
4const app = createApp({
5  cors: true,        // Enable CORS
6  compression: true, // Enable gzip compression
7  helmet: true       // Enable security headers
8});
9
10// Input validation with Zod (built-in)
11app.post('/users')
12  .body(z.object({
13    name: z.string().min(2).max(50),
14    email: z.string().email(),
15    password: z.string().min(8)
16  }))
17  .handler((req, res) => {
18    // Input is automatically validated and sanitized
19    const { name, email, password } = req.body;
20    
21    // Hash password before storing (use bcrypt library)
22    const hashedPassword = hashPassword(password);
23    
24    const user = createUser({
25      name,
26      email,
27      password: hashedPassword
28    });
29    
30    return { success: true, data: user };
31  });
32
33// Rate limiting (chainable API)
34app.post('/auth/login')
35  .rateLimit({ requests: 5, window: 900000 }) // 5 attempts per 15 minutes
36  .handler((req, res) => {
37    return authenticateUser(req.body);
38  });

What's Included

  • • CORS support (simple boolean flag)
  • • Helmet security headers
  • • Gzip compression
  • • Input validation with Zod
  • • Rate limiting (per-route)
  • • WebSocket support

Additional Security

For advanced security, integrate these libraries:

  • bcrypt - Password hashing
  • jsonwebtoken - JWT authentication
  • express-rate-limit - Advanced rate limiting
  • express-validator - Additional validation
  • passport - Authentication strategies

Security Headers (Helmet)

Comprehensive Security Headers

typescript

1import { createApp } from '@morojs/moro';
2
3const app = createApp({
4  security: {
5    helmet: {
6      // Content Security Policy
7      contentSecurityPolicy: {
8        directives: {
9          defaultSrc: ["'self'"],
10          scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.example.com"],
11          styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
12          imgSrc: ["'self'", "data:", "https:"],
13          connectSrc: ["'self'", "https://api.example.com"],
14          fontSrc: ["'self'", "https://fonts.gstatic.com"],
15          objectSrc: ["'none'"],
16          mediaSrc: ["'self'"],
17          frameSrc: ["'none'"],
18        },
19        reportOnly: false, // Set to true for testing
20        reportUri: '/csp-report'
21      },
22      
23      // HTTP Strict Transport Security
24      hsts: {
25        maxAge: 31536000, // 1 year
26        includeSubDomains: true,
27        preload: true
28      },
29      
30      // X-Frame-Options
31      frameguard: {
32        action: 'deny' // or 'sameorigin'
33      },
34      
35      // X-Content-Type-Options
36      noSniff: true,
37      
38      // X-XSS-Protection
39      xssFilter: true,
40      
41      // Referrer Policy
42      referrerPolicy: { policy: "strict-origin-when-cross-origin" },
43      
44      // Hide X-Powered-By header
45      hidePoweredBy: true,
46      
47      // DNS Prefetch Control
48      dnsPrefetchControl: { allow: false },
49      
50      // IE No Open
51      ieNoOpen: true,
52      
53      // Cross-Origin Embedder Policy
54      crossOriginEmbedderPolicy: true,
55      
56      // Cross-Origin Opener Policy
57      crossOriginOpenerPolicy: { policy: "same-origin" },
58      
59      // Cross-Origin Resource Policy
60      crossOriginResourcePolicy: { policy: "cross-origin" },
61      
62      // Origin Agent Cluster
63      originAgentCluster: true,
64      
65      // Permitted Cross-Domain Policies
66      permittedCrossDomainPolicies: false
67    }
68  }
69});

Environment-Specific Security

typescript

1const getSecurityConfig = () => {
2  const isProd = process.env.NODE_ENV === 'production';
3  
4  return {
5    helmet: {
6      contentSecurityPolicy: {
7        directives: {
8          defaultSrc: ["'self'"],
9          scriptSrc: isProd 
10            ? ["'self'"] 
11            : ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
12          styleSrc: ["'self'", "'unsafe-inline'"],
13          imgSrc: ["'self'", "data:", "https:"],
14          connectSrc: isProd
15            ? ["'self'", "https://api.example.com"]
16            : ["'self'", "http://localhost:*", "ws://localhost:*"]
17        },
18        reportOnly: !isProd
19      },
20      hsts: isProd ? {
21        maxAge: 31536000,
22        includeSubDomains: true,
23        preload: true
24      } : false
25    }
26  };
27};
28
29const app = createApp({
30  security: getSecurityConfig()
31});

Authentication & Authorization

JWT Authentication Middleware

typescript

1import { jwt, requireAuth, requireRole } from '@morojs/moro/auth';
2
3// JWT configuration
4const app = createApp({
5  auth: {
6    jwt: {
7      secret: process.env.JWT_SECRET,
8      algorithm: 'HS256',
9      expiresIn: '1h',
10      issuer: 'myapp.com',
11      audience: 'myapp-users'
12    }
13  }
14});
15
16// JWT validation middleware
17const jwtAuth = jwt({
18  secret: process.env.JWT_SECRET,
19  algorithms: ['HS256'],
20  credentialsRequired: true,
21  getToken: (req) => {
22    // Extract from Authorization header
23    const authHeader = req.headers.authorization;
24    if (authHeader && authHeader.startsWith('Bearer ')) {
25      return authHeader.substring(7);
26    }
27    
28    // Or from cookie
29    return req.cookies?.token;
30  }
31});
32
33// Protected routes
34app.get('/profile', {
35  middleware: [jwtAuth],
36  handler: ({ context }) => {
37    const user = context.user; // Populated by JWT middleware
38    return {
39      id: user.id,
40      email: user.email,
41      name: user.name
42    };
43  }
44});
45
46// Role-based access control
47app.post('/admin/users', {
48  middleware: [
49    jwtAuth,
50    requireRole(['admin', 'super-admin'])
51  ],
52  handler: createUser
53});
54
55// Permission-based access control
56app.delete('/posts/:id', {
57  middleware: [
58    jwtAuth,
59    requirePermission('posts:delete')
60  ],
61  handler: deletePost
62});

OAuth 2.0 / OpenID Connect

typescript

1import { oauth } from '@morojs/moro/auth';
2
3// OAuth configuration
4const oauthConfig = {
5  google: {
6    clientId: process.env.GOOGLE_CLIENT_ID,
7    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
8    redirectUri: 'https://myapp.com/auth/google/callback',
9    scopes: ['openid', 'profile', 'email']
10  },
11  github: {
12    clientId: process.env.GITHUB_CLIENT_ID,
13    clientSecret: process.env.GITHUB_CLIENT_SECRET,
14    redirectUri: 'https://myapp.com/auth/github/callback',
15    scopes: ['user:email']
16  }
17};
18
19// OAuth routes
20app.get('/auth/:provider', {
21  handler: ({ params }) => {
22    const { provider } = params;
23    const config = oauthConfig[provider];
24    
25    if (!config) {
26      return { error: 'Unsupported provider' };
27    }
28    
29    const authUrl = oauth.getAuthorizationUrl(provider, config);
30    return { redirect: authUrl };
31  }
32});
33
34app.get('/auth/:provider/callback', {
35  handler: async ({ params, query }) => {
36    const { provider } = params;
37    const { code, state } = query;
38    
39    try {
40      const tokenData = await oauth.exchangeCodeForToken(provider, code);
41      const userInfo = await oauth.getUserInfo(provider, tokenData.access_token);
42      
43      // Create or update user in database
44      const user = await createOrUpdateUser(userInfo);
45      
46      // Generate JWT token
47      const jwt = await generateJWT(user);
48      
49      return {
50        success: true,
51        token: jwt,
52        user: {
53          id: user.id,
54          email: user.email,
55          name: user.name
56        }
57      };
58    } catch (error) {
59      return { error: 'Authentication failed' };
60    }
61  }
62});

Rate Limiting & DDoS Protection

Advanced Rate Limiting

typescript

1import { rateLimit } from '@morojs/moro/middleware';
2
3// Global rate limiting
4app.use(rateLimit({
5  windowMs: 15 * 60 * 1000, // 15 minutes
6  max: 1000, // Limit each IP to 1000 requests per windowMs
7  message: {
8    error: 'Too many requests from this IP, please try again later.',
9    retryAfter: 900 // seconds
10  },
11  
12  // Custom key generator
13  keyGenerator: (req) => {
14    // Rate limit by user ID if authenticated, otherwise by IP
15    return req.user?.id || req.ip;
16  },
17  
18  // Skip certain requests
19  skip: (req) => {
20    return req.path === '/health' || req.path === '/metrics';
21  },
22  
23  // Custom handler for rate limit exceeded
24  handler: (req, res) => {
25    res.status(429).json({
26      error: 'Rate limit exceeded',
27      retryAfter: Math.round(req.rateLimit.resetTime / 1000),
28      limit: req.rateLimit.limit,
29      current: req.rateLimit.current,
30      remaining: req.rateLimit.remaining
31    });
32  },
33  
34  // Store for distributed rate limiting
35  store: new RedisStore({
36    client: redis,
37    prefix: 'rl:'
38  })
39}));
40
41// API-specific rate limiting
42app.use('/api/', rateLimit({
43  windowMs: 1 * 60 * 1000, // 1 minute
44  max: 100, // 100 requests per minute for API
45  standardHeaders: true,
46  legacyHeaders: false
47}));
48
49// Strict rate limiting for authentication endpoints
50app.use('/auth/', rateLimit({
51  windowMs: 15 * 60 * 1000, // 15 minutes
52  max: 5, // Only 5 login attempts per 15 minutes
53  skipSuccessfulRequests: true,
54  
55  // Progressive delays
56  onLimitReached: (req, res, options) => {
57    // Log security event
58    logger.warn('Rate limit exceeded for auth endpoint', {
59      ip: req.ip,
60      userAgent: req.get('User-Agent'),
61      path: req.path
62    });
63  }
64}));

DDoS Protection

typescript

1import { ddosProtection } from '@morojs/moro/security';
2
3// DDoS protection middleware
4app.use(ddosProtection({
5  // Maximum number of connections per IP
6  burst: 10,
7  limit: 15,
8  
9  // Time window for connection tracking
10  maxconnections: 100,
11  maxrequestspersecond: 20,
12  
13  // Whitelist trusted IPs
14  whitelist: [
15    '127.0.0.1',
16    '::1',
17    // Add your trusted IPs/ranges
18    '10.0.0.0/8',
19    '172.16.0.0/12',
20    '192.168.0.0/16'
21  ],
22  
23  // Blacklist known bad IPs
24  blacklist: [
25    // IPs will be added dynamically
26  ],
27  
28  // Response for blocked requests
29  errorData: {
30    error: 'Request blocked due to suspicious activity',
31    code: 'DDOS_PROTECTION'
32  },
33  
34  // Callback for blocked requests
35  onDenial: (req) => {
36    logger.warn('DDoS protection triggered', {
37      ip: req.ip,
38      userAgent: req.get('User-Agent'),
39      path: req.path,
40      timestamp: new Date().toISOString()
41    });
42    
43    // Optionally add to blacklist
44    if (req.ddos.weight > 50) {
45      ddosProtection.addToBlacklist(req.ip, 3600); // 1 hour
46    }
47  }
48}));

Input Validation & Sanitization

Comprehensive Input Validation

typescript

1import { z } from 'zod';
2import { sanitize } from '@morojs/moro/security';
3
4// Custom sanitization schemas
5const SanitizedString = z.string().transform((val) => {
6  // Remove HTML tags and dangerous characters
7  return sanitize.html(val, {
8    allowedTags: [],
9    allowedAttributes: {}
10  });
11});
12
13const EmailSchema = z.string()
14  .email('Invalid email format')
15  .transform((val) => val.toLowerCase().trim());
16
17const PasswordSchema = z.string()
18  .min(8, 'Password must be at least 8 characters')
19  .regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/, 
20    'Password must contain uppercase, lowercase, number, and special character');
21
22// User registration with validation
23const CreateUserSchema = z.object({
24  email: EmailSchema,
25  password: PasswordSchema,
26  name: SanitizedString.min(2).max(50),
27  bio: SanitizedString.max(500).optional(),
28  website: z.string().url().optional().transform((val) => {
29    // Ensure HTTPS for external links
30    if (val && !val.startsWith('https://')) {
31      return `https://${val.replace(/^https?:\/\//, '')}`;
32    }
33    return val;
34  })
35});
36
37app.post('/users', {
38  body: CreateUserSchema,
39  handler: async ({ body }) => {
40    // All input is automatically validated and sanitized
41    const { email, password, name, bio, website } = body;
42    
43    // Additional security checks
44    if (await isEmailBlacklisted(email)) {
45      throw new Error('Email domain not allowed');
46    }
47    
48    // Hash password
49    const hashedPassword = await bcrypt.hash(password, 12);
50    
51    const user = await createUser({
52      email,
53      password: hashedPassword,
54      name,
55      bio,
56      website
57    });
58    
59    return { id: user.id, email: user.email, name: user.name };
60  }
61});

SQL Injection Prevention

typescript

1// MoroJS automatically uses parameterized queries
2import { db } from '@morojs/moro/database';
3
4// Safe database queries (automatically parameterized)
5app.get('/users/:id', {
6  params: z.object({
7    id: z.string().uuid('Invalid user ID format')
8  }),
9  handler: async ({ params }) => {
10    // This is automatically safe from SQL injection
11    const user = await db.query(
12      'SELECT id, email, name FROM users WHERE id = $1',
13      [params.id]
14    );
15    
16    return user[0] || null;
17  }
18});
19
20// Search with additional validation
21app.get('/users/search', {
22  query: z.object({
23    q: z.string()
24      .min(2, 'Search query too short')
25      .max(100, 'Search query too long')
26      .transform((val) => {
27        // Remove dangerous characters for search
28        return val.replace(/[<>"'%;()&+]/g, '');
29      })
30  }),
31  handler: async ({ query }) => {
32    // Use full-text search with proper escaping
33    const results = await db.query(
34      'SELECT id, name, email FROM users WHERE search_vector @@ plainto_tsquery($1) LIMIT 20',
35      [query.q]
36    );
37    
38    return results;
39  }
40});

Security Monitoring & Logging

Security Event Logging

typescript

1import { securityLogger } from '@morojs/moro/security';
2
3// Security event middleware
4const securityMonitoring = (req, res, next) => {
5  // Log suspicious patterns
6  const suspiciousPatterns = [
7    /\.\.\//, // Directory traversal
8    /<script/i, // XSS attempts
9    /union.*select/i, // SQL injection
10    /eval\s*\(/i, // Code injection
11    /javascript:/i // JavaScript protocol
12  ];
13  
14  const userAgent = req.get('User-Agent') || '';
15  const url = req.url;
16  const body = JSON.stringify(req.body);
17  
18  // Check for suspicious patterns
19  const suspicious = suspiciousPatterns.some(pattern => 
20    pattern.test(url) || pattern.test(body) || pattern.test(userAgent)
21  );
22  
23  if (suspicious) {
24    securityLogger.warn('Suspicious request detected', {
25      ip: req.ip,
26      userAgent,
27      url,
28      method: req.method,
29      body: req.body,
30      headers: req.headers,
31      timestamp: new Date().toISOString(),
32      severity: 'HIGH'
33    });
34    
35    // Optional: Block the request
36    // return res.status(400).json({ error: 'Request blocked' });
37  }
38  
39  // Log failed authentication attempts
40  res.on('finish', () => {
41    if (req.path.includes('/auth/') && res.statusCode === 401) {
42      securityLogger.warn('Failed authentication attempt', {
43        ip: req.ip,
44        userAgent,
45        path: req.path,
46        timestamp: new Date().toISOString()
47      });
48    }
49  });
50  
51  next();
52};
53
54app.use(securityMonitoring);

Real-time Security Alerts

typescript

1import { SecurityMonitor } from '@morojs/moro/security';
2
3const monitor = new SecurityMonitor({
4  // Alert thresholds
5  thresholds: {
6    failedLogins: { count: 5, window: '5m' },
7    suspiciousRequests: { count: 10, window: '1m' },
8    rateLimitExceeded: { count: 3, window: '15m' }
9  },
10  
11  // Alert channels
12  alerts: {
13    email: {
14      enabled: true,
15      recipients: ['security@example.com'],
16      smtp: {
17        host: process.env.SMTP_HOST,
18        auth: {
19          user: process.env.SMTP_USER,
20          pass: process.env.SMTP_PASS
21        }
22      }
23    },
24    slack: {
25      enabled: true,
26      webhook: process.env.SLACK_WEBHOOK_URL,
27      channel: '#security-alerts'
28    },
29    webhook: {
30      enabled: true,
31      url: process.env.SECURITY_WEBHOOK_URL,
32      headers: {
33        'Authorization': `Bearer ${process.env.WEBHOOK_TOKEN}`
34      }
35    }
36  }
37});
38
39// Monitor security events
40monitor.on('failed-login-threshold', (data) => {
41  monitor.alert('HIGH', 'Multiple failed login attempts detected', {
42    ip: data.ip,
43    attempts: data.count,
44    timeWindow: data.window,
45    action: 'Consider blocking IP address'
46  });
47});
48
49monitor.on('suspicious-activity', (data) => {
50  monitor.alert('CRITICAL', 'Potential attack detected', {
51    pattern: data.pattern,
52    ip: data.ip,
53    requests: data.requests,
54    action: 'Immediate investigation required'
55  });
56});

Security Best Practices

Security Checklist

  • • Use HTTPS in production
  • • Validate and sanitize all inputs
  • • Implement proper authentication
  • • Use parameterized queries
  • • Set security headers
  • • Enable rate limiting
  • • Monitor security events
  • • Keep dependencies updated

Common Vulnerabilities

  • • SQL Injection
  • • Cross-Site Scripting (XSS)
  • • Cross-Site Request Forgery (CSRF)
  • • Insecure Direct Object References
  • • Security Misconfiguration
  • • Broken Authentication
  • • Sensitive Data Exposure
  • • Insufficient Logging & Monitoring

Security Configuration Checklist

typescript

1// Production security configuration
2const productionSecurityConfig = {
3  // Force HTTPS
4  server: {
5    https: {
6      enabled: true,
7      redirectHttp: true,
8      hsts: {
9        maxAge: 31536000,
10        includeSubDomains: true,
11        preload: true
12      }
13    }
14  },
15  
16  // Security headers
17  helmet: {
18    contentSecurityPolicy: {
19      directives: {
20        defaultSrc: ["'self'"],
21        scriptSrc: ["'self'"],
22        styleSrc: ["'self'", "'unsafe-inline'"],
23        imgSrc: ["'self'", "data:", "https:"],
24        connectSrc: ["'self'"],
25        fontSrc: ["'self'"],
26        objectSrc: ["'none'"],
27        mediaSrc: ["'self'"],
28        frameSrc: ["'none'"]
29      }
30    },
31    frameguard: { action: 'deny' },
32    noSniff: true,
33    xssFilter: true,
34    referrerPolicy: { policy: 'strict-origin-when-cross-origin' }
35  },
36  
37  // Rate limiting
38  rateLimit: {
39    global: { max: 1000, window: '15m' },
40    auth: { max: 5, window: '15m' },
41    api: { max: 100, window: '15m' }
42  },
43  
44  // CORS
45  cors: {
46    origin: ['https://yourdomain.com'],
47    credentials: true,
48    methods: ['GET', 'POST', 'PUT', 'DELETE'],
49    allowedHeaders: ['Content-Type', 'Authorization']
50  },
51  
52  // Authentication
53  auth: {
54    jwt: {
55      secret: process.env.JWT_SECRET, // Use strong secret
56      algorithm: 'HS256',
57      expiresIn: '1h'
58    }
59  },
60  
61  // Logging
62  logging: {
63    level: 'info',
64    security: {
65      enabled: true,
66      logFailedAttempts: true,
67      logSuspiciousActivity: true
68    }
69  }
70};

Next Steps