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};