Coherent.js API Security Guide
November 17, 2025 ยท View on GitHub
This guide covers the comprehensive security features built into the Coherent.js API framework and best practices for secure API development.
Table of Contents
- Built-in Security Features
- Authentication & Authorization
- Input Validation & Sanitization
- Rate Limiting & DoS Protection
- Security Headers
- CORS Configuration
- Request Size Limits
- Password Security
- Security Best Practices
- Common Vulnerabilities Prevention
Built-in Security Features
The Coherent.js API framework includes enterprise-grade security features out of the box:
- Automatic Security Headers - CORS, XSS protection, content type sniffing prevention
- Rate Limiting - IP-based request throttling with configurable windows
- Input Sanitization - XSS prevention and prototype pollution protection
- Request Size Limits - Protection against large payload attacks
- Authentication Middleware - JWT-based authentication with role support
- Input Validation - JSON Schema validation with security considerations
Authentication & Authorization
JWT Token Authentication
const { withAuth, withRole, generateToken } = require('../src/api/security');
// Generate tokens
const token = generateToken({
userId: 123,
username: 'john_doe',
role: 'user',
permissions: ['read', 'write']
}, '24h'); // Token expires in 24 hours
// Protected routes
const routes = {
api: {
profile: {
GET: {
middleware: [withAuth],
handler: async (req, res) => {
// req.user contains decoded token data
return { user: req.user };
}
}
},
admin: {
GET: {
middleware: [withAuth, withRole('admin')],
handler: async (req, res) => {
return { message: 'Admin access granted' };
}
}
}
}
};
Custom Authentication
const customAuth = async (req, res) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey || !await validateApiKey(apiKey)) {
res.statusCode = 401;
throw new Error('Invalid API key');
}
req.user = await getUserByApiKey(apiKey);
};
const routes = {
api: {
data: {
GET: {
middleware: [customAuth],
handler: async (req, res) => {
return { data: 'Protected data' };
}
}
}
}
};
Input Validation & Sanitization
JSON Schema Validation
const { withValidation } = require('../src/api/security');
const userSchema = {
type: 'object',
properties: {
username: {
type: 'string',
minLength: 3,
maxLength: 30,
pattern: '^[a-zA-Z0-9_]+$' // Alphanumeric and underscore only
},
email: {
type: 'string',
format: 'email',
maxLength: 255
},
age: {
type: 'integer',
minimum: 13,
maximum: 120
}
},
required: ['username', 'email'],
additionalProperties: false // Prevent extra properties
};
const routes = {
api: {
users: {
POST: {
middleware: [withValidation(userSchema)],
handler: async (req, res) => {
// req.body is validated and sanitized
return { success: true, user: req.body };
}
}
}
}
};
Custom Validation
const validateUserInput = async (req, res) => {
const { username, email } = req.body;
// Custom validation logic
if (await userExists(username)) {
res.statusCode = 409;
throw new Error('Username already exists');
}
if (await isEmailBlacklisted(email)) {
res.statusCode = 400;
throw new Error('Email domain not allowed');
}
};
const routes = {
api: {
register: {
POST: {
middleware: [withValidation(userSchema), validateUserInput],
handler: async (req, res) => {
return await createUser(req.body);
}
}
}
}
};
Rate Limiting & DoS Protection
Basic Rate Limiting
const server = router.createServer({
rateLimit: {
windowMs: 60000, // 1 minute window
maxRequests: 100 // 100 requests per minute per IP
}
});
Advanced Rate Limiting
// Different limits for different endpoints
const rateLimitConfig = {
'/api/login': { windowMs: 300000, maxRequests: 5 }, // 5 login attempts per 5 minutes
'/api/upload': { windowMs: 60000, maxRequests: 10 }, // 10 uploads per minute
'/api/search': { windowMs: 60000, maxRequests: 1000 } // 1000 searches per minute
};
// Custom rate limiting middleware
const customRateLimit = (endpoint) => {
const config = rateLimitConfig[endpoint] || { windowMs: 60000, maxRequests: 100 };
return async (req, res) => {
const clientIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
if (!checkRateLimit(clientIP, config.windowMs, config.maxRequests)) {
res.statusCode = 429;
throw new Error('Too Many Requests');
}
};
};
Security Headers
Default Security Headers
The framework automatically adds these security headers:
// Automatically added to all responses:
{
'Access-Control-Allow-Origin': 'http://localhost:3000', // Configurable
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'X-XSS-Protection': '1; mode=block',
'Content-Security-Policy': "default-src 'self'"
}
Custom Security Headers
const addCustomHeaders = async (req, res) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
res.setHeader('X-Permitted-Cross-Domain-Policies', 'none');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
};
const routes = {
api: {
secure: {
GET: {
middleware: [addCustomHeaders],
handler: async (req, res) => {
return { message: 'Secure endpoint' };
}
}
}
}
};
CORS Configuration
Basic CORS Setup
const server = router.createServer({
corsOrigin: 'https://yourdomain.com' // Single origin
});
Multiple Origins
const server = router.createServer({
corsOrigin: [
'https://app.yourdomain.com',
'https://admin.yourdomain.com',
'https://mobile.yourdomain.com'
]
});
Dynamic CORS
const dynamicCors = async (req, res) => {
const origin = req.headers.origin;
const allowedOrigins = await getAllowedOrigins(); // From database
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
};
Request Size Limits
Basic Size Limits
const server = router.createServer({
maxBodySize: 5 * 1024 * 1024 // 5MB limit
});
Endpoint-Specific Limits
const checkFileUploadSize = async (req, res) => {
const contentLength = parseInt(req.headers['content-length'] || '0');
const maxSize = 50 * 1024 * 1024; // 50MB for file uploads
if (contentLength > maxSize) {
res.statusCode = 413;
throw new Error('File too large');
}
};
const routes = {
api: {
upload: {
POST: {
middleware: [checkFileUploadSize],
handler: async (req, res) => {
return await handleFileUpload(req.body);
}
}
}
}
};
Password Security
Password Hashing
const { hashPassword, verifyPassword } = require('../src/api/security');
// Registration
const registerUser = async (req, res) => {
const { username, password } = req.body;
// Hash password before storing
const hashedPassword = await hashPassword(password);
const user = await createUser({
username,
password: hashedPassword
});
return { success: true, userId: user.id };
};
// Login
const loginUser = async (req, res) => {
const { username, password } = req.body;
const user = await getUserByUsername(username);
if (!user || !await verifyPassword(password, user.password)) {
res.statusCode = 401;
throw new Error('Invalid credentials');
}
const token = generateToken({ userId: user.id, username });
return { token, user: { id: user.id, username } };
};
Password Policies
const validatePassword = (password) => {
const minLength = 8;
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
const hasNumbers = /\d/.test(password);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
if (password.length < minLength) {
throw new Error('Password must be at least 8 characters long');
}
if (!hasUpperCase || !hasLowerCase || !hasNumbers || !hasSpecialChar) {
throw new Error('Password must contain uppercase, lowercase, numbers, and special characters');
}
};
Security Best Practices
1. Input Validation
- Always validate and sanitize user input
- Use JSON Schema for structured validation
- Implement whitelist-based validation
- Reject unexpected data structures
2. Authentication
- Use strong JWT secrets (256-bit minimum)
- Implement token expiration
- Use refresh tokens for long-lived sessions
- Store sensitive data server-side only
3. Authorization
- Implement role-based access control
- Use principle of least privilege
- Validate permissions on every request
- Audit access patterns regularly
4. Data Protection
- Hash passwords with salt
- Encrypt sensitive data at rest
- Use HTTPS for all communications
- Implement proper session management
5. Error Handling
- Don't expose internal errors to clients
- Log security events for monitoring
- Implement proper error responses
- Use consistent error formats
6. Monitoring
- Log authentication attempts
- Monitor rate limiting triggers
- Track unusual access patterns
- Set up security alerts
Common Vulnerabilities Prevention
SQL Injection Prevention
// Use parameterized queries
const getUserById = async (id) => {
// Good: Parameterized query
return await db.query('SELECT * FROM users WHERE id = ?', [id]);
// Bad: String concatenation
// return await db.query(`SELECT * FROM users WHERE id = ${id}`);
};
XSS Prevention
// Input sanitization is automatic, but for HTML content:
const sanitizeHtml = (html) => {
return html
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/');
};
CSRF Prevention
const csrfProtection = async (req, res) => {
const token = req.headers['x-csrf-token'];
const sessionToken = req.session?.csrfToken;
if (!token || token !== sessionToken) {
res.statusCode = 403;
throw new Error('Invalid CSRF token');
}
};
Prototype Pollution Prevention
// Automatic sanitization prevents prototype pollution
// But for additional safety:
const safeObjectAssign = (target, source) => {
const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
Object.keys(source).forEach(key => {
if (!dangerousKeys.includes(key)) {
target[key] = source[key];
}
});
return target;
};
Security Testing
Testing Security Features
// Example security test
const testSecurity = async () => {
// Test rate limiting
const responses = await Promise.all(
Array(110).fill().map(() => fetch('/api/test'))
);
const rateLimited = responses.some(r => r.status === 429);
console.log('Rate limiting:', rateLimited ? 'PASS' : 'FAIL');
// Test XSS protection
const xssPayload = { name: '<script>alert("xss")</script>' };
const xssResponse = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(xssPayload)
});
console.log('XSS protection:', xssResponse.ok ? 'PASS' : 'FAIL');
// Test authentication
const protectedResponse = await fetch('/api/protected');
console.log('Auth protection:', protectedResponse.status === 401 ? 'PASS' : 'FAIL');
};
Conclusion
The Coherent.js API framework provides comprehensive security features out of the box, but security is a shared responsibility. Always:
- Keep the framework updated
- Follow security best practices
- Regularly audit your code
- Monitor security logs
- Test security features
- Stay informed about new threats
For additional security questions or to report vulnerabilities, please refer to the project's security policy.