Security Templates
Template Version: 2.1
Last Updated: 2024-12-19
Applicable To: Authentication, authorization, security policies, compliance
Template Overview
This collection provides comprehensive security templates and configurations for implementing robust security measures in modern applications. All templates follow OWASP guidelines and industry best practices.
Security Framework Overview
Authentication Templates
JWT Authentication Service
// auth/jwt-service.js
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const crypto = require('crypto');
class JWTAuthService {
constructor(config) {
this.accessTokenSecret = config.accessTokenSecret;
this.refreshTokenSecret = config.refreshTokenSecret;
this.accessTokenExpiry = config.accessTokenExpiry || '15m';
this.refreshTokenExpiry = config.refreshTokenExpiry || '7d';
}
async hashPassword(password) {
const saltRounds = 12;
return await bcrypt.hash(password, saltRounds);
}
async verifyPassword(password, hashedPassword) {
return await bcrypt.compare(password, hashedPassword);
}
generateTokenPair(user) {
const payload = {
userId: user.id,
email: user.email,
role: user.role,
permissions: user.permissions
};
const accessToken = jwt.sign(
payload,
this.accessTokenSecret,
{
expiresIn: this.accessTokenExpiry,
issuer: 'myapp',
audience: 'myapp-users'
}
);
const refreshToken = jwt.sign(
{ userId: user.id, tokenVersion: user.tokenVersion },
this.refreshTokenSecret,
{
expiresIn: this.refreshTokenExpiry,
issuer: 'myapp'
}
);
return { accessToken, refreshToken };
}
verifyAccessToken(token) {
try {
return jwt.verify(token, this.accessTokenSecret);
} catch (error) {
throw new Error('Invalid access token');
}
}
verifyRefreshToken(token) {
try {
return jwt.verify(token, this.refreshTokenSecret);
} catch (error) {
throw new Error('Invalid refresh token');
}
}
generateSecureRandomToken() {
return crypto.randomBytes(32).toString('hex');
}
}
module.exports = JWTAuthService;
Authentication Middleware
// middleware/auth.js
const JWTAuthService = require('../auth/jwt-service');
class AuthenticationMiddleware {
constructor(authService) {
this.authService = authService;
}
authenticate() {
return async (req, res, next) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
error: {
code: 'UNAUTHORIZED',
message: 'Access token required'
}
});
}
const token = authHeader.substring(7);
const decoded = this.authService.verifyAccessToken(token);
// Attach user info to request
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({
error: {
code: 'INVALID_TOKEN',
message: 'Invalid or expired token'
}
});
}
};
}
requireRole(requiredRoles) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
error: {
code: 'UNAUTHORIZED',
message: 'Authentication required'
}
});
}
const userRoles = Array.isArray(req.user.role) ? req.user.role : [req.user.role];
const hasRequiredRole = requiredRoles.some(role => userRoles.includes(role));
if (!hasRequiredRole) {
return res.status(403).json({
error: {
code: 'FORBIDDEN',
message: 'Insufficient permissions'
}
});
}
next();
};
}
requirePermission(requiredPermissions) {
return (req, res, next) => {
if (!req.user || !req.user.permissions) {
return res.status(403).json({
error: {
code: 'FORBIDDEN',
message: 'Insufficient permissions'
}
});
}
const userPermissions = req.user.permissions;
const hasPermission = requiredPermissions.every(permission =>
userPermissions.includes(permission)
);
if (!hasPermission) {
return res.status(403).json({
error: {
code: 'FORBIDDEN',
message: 'Required permissions not granted'
}
});
}
next();
};
}
}
module.exports = AuthenticationMiddleware;
Authorization Templates
Role-Based Access Control (RBAC)
// rbac/rbac-service.js
class RBACService {
constructor() {
this.roles = new Map();
this.permissions = new Map();
this.setupDefaultRoles();
}
setupDefaultRoles() {
// Define permissions
const permissions = {
// User management
'users:read': 'Read user information',
'users:write': 'Create and update users',
'users:delete': 'Delete users',
// Content management
'content:read': 'Read content',
'content:write': 'Create and update content',
'content:delete': 'Delete content',
'content:publish': 'Publish content',
// System administration
'system:admin': 'System administration access',
'system:monitor': 'System monitoring access',
'system:backup': 'System backup access'
};
// Store permissions
Object.entries(permissions).forEach(([key, description]) => {
this.permissions.set(key, { name: key, description });
});
// Define roles with their permissions
this.addRole('guest', 'Guest User', [
'content:read'
]);
this.addRole('user', 'Regular User', [
'content:read',
'users:read'
]);
this.addRole('editor', 'Content Editor', [
'content:read',
'content:write',
'users:read'
]);
this.addRole('admin', 'Administrator', [
'users:read',
'users:write',
'users:delete',
'content:read',
'content:write',
'content:delete',
'content:publish',
'system:admin',
'system:monitor',
'system:backup'
]);
}
addRole(name, description, permissions = []) {
this.roles.set(name, {
name,
description,
permissions: new Set(permissions)
});
}
addPermissionToRole(roleName, permission) {
const role = this.roles.get(roleName);
if (role) {
role.permissions.add(permission);
}
}
removePermissionFromRole(roleName, permission) {
const role = this.roles.get(roleName);
if (role) {
role.permissions.delete(permission);
}
}
hasPermission(userRoles, requiredPermission) {
if (!Array.isArray(userRoles)) {
userRoles = [userRoles];
}
return userRoles.some(roleName => {
const role = this.roles.get(roleName);
return role && role.permissions.has(requiredPermission);
});
}
hasAllPermissions(userRoles, requiredPermissions) {
return requiredPermissions.every(permission =>
this.hasPermission(userRoles, permission)
);
}
getUserPermissions(userRoles) {
if (!Array.isArray(userRoles)) {
userRoles = [userRoles];
}
const permissions = new Set();
userRoles.forEach(roleName => {
const role = this.roles.get(roleName);
if (role) {
role.permissions.forEach(permission => permissions.add(permission));
}
});
return Array.from(permissions);
}
}
module.exports = RBACService;
Policy-Based Access Control
// rbac/policy-service.js
class PolicyService {
constructor() {
this.policies = new Map();
this.setupDefaultPolicies();
}
setupDefaultPolicies() {
// Owner-based access
this.addPolicy('resource:owner', (context) => {
return context.user.id === context.resource.ownerId;
});
// Team-based access
this.addPolicy('resource:team', (context) => {
return context.user.teamId === context.resource.teamId;
});
// Time-based access
this.addPolicy('access:business_hours', (context) => {
const now = new Date();
const hour = now.getHours();
return hour >= 9 && hour <= 17; // 9 AM to 5 PM
});
// IP-based access
this.addPolicy('access:internal_network', (context) => {
const allowedNetworks = ['192.168.1.0/24', '10.0.0.0/8'];
return this.isIPInNetworks(context.clientIP, allowedNetworks);
});
// Hierarchical organization access
this.addPolicy('org:hierarchy', (context) => {
return this.hasHierarchicalAccess(
context.user.organizationPath,
context.resource.organizationPath
);
});
}
addPolicy(name, evaluator) {
this.policies.set(name, {
name,
evaluate: evaluator
});
}
async evaluatePolicy(policyName, context) {
const policy = this.policies.get(policyName);
if (!policy) {
throw new Error(`Policy '${policyName}' not found`);
}
try {
return await policy.evaluate(context);
} catch (error) {
console.error(`Error evaluating policy '${policyName}':`, error);
return false; // Fail closed
}
}
async evaluateMultiplePolicies(policyNames, context, mode = 'all') {
const results = await Promise.all(
policyNames.map(name => this.evaluatePolicy(name, context))
);
return mode === 'all'
? results.every(result => result === true)
: results.some(result => result === true);
}
isIPInNetworks(ip, networks) {
// Simplified IP network checking - use proper library in production
return networks.some(network => {
if (network.includes('/')) {
// CIDR notation - implement proper CIDR checking
return ip.startsWith(network.split('/')[0].split('.').slice(0, 3).join('.'));
}
return ip === network;
});
}
hasHierarchicalAccess(userPath, resourcePath) {
// Check if user has access based on organizational hierarchy
// e.g., "/org/department/team" can access "/org/department/team/project"
return resourcePath.startsWith(userPath);
}
}
module.exports = PolicyService;
Data Protection Templates
Encryption Service
// security/encryption-service.js
const crypto = require('crypto');
class EncryptionService {
constructor(config) {
this.algorithm = 'aes-256-gcm';
this.keyLength = 32;
this.ivLength = 16;
this.saltLength = 16;
this.tagLength = 16;
this.iterations = 100000;
// Use environment variable for master key
this.masterKey = config.masterKey || process.env.MASTER_ENCRYPTION_KEY;
if (!this.masterKey) {
throw new Error('Master encryption key is required');
}
}
// Encrypt sensitive data
encryptData(plaintext, additionalData = '') {
try {
const salt = crypto.randomBytes(this.saltLength);
const iv = crypto.randomBytes(this.ivLength);
// Derive key from master key and salt
const key = crypto.pbkdf2Sync(
Buffer.from(this.masterKey, 'utf8'),
salt,
this.iterations,
this.keyLength,
'sha256'
);
const cipher = crypto.createCipher(this.algorithm, key);
cipher.setAAD(Buffer.from(additionalData, 'utf8'));
let encrypted = cipher.update(plaintext, 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
const tag = cipher.getAuthTag();
// Combine all components
const result = Buffer.concat([
salt,
iv,
tag,
encrypted
]);
return result.toString('base64');
} catch (error) {
throw new Error('Encryption failed: ' + error.message);
}
}
// Decrypt sensitive data
decryptData(encryptedData, additionalData = '') {
try {
const buffer = Buffer.from(encryptedData, 'base64');
// Extract components
const salt = buffer.subarray(0, this.saltLength);
const iv = buffer.subarray(this.saltLength, this.saltLength + this.ivLength);
const tag = buffer.subarray(
this.saltLength + this.ivLength,
this.saltLength + this.ivLength + this.tagLength
);
const encrypted = buffer.subarray(this.saltLength + this.ivLength + this.tagLength);
// Derive key
const key = crypto.pbkdf2Sync(
Buffer.from(this.masterKey, 'utf8'),
salt,
this.iterations,
this.keyLength,
'sha256'
);
const decipher = crypto.createDecipher(this.algorithm, key);
decipher.setAuthTag(tag);
decipher.setAAD(Buffer.from(additionalData, 'utf8'));
let decrypted = decipher.update(encrypted);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString('utf8');
} catch (error) {
throw new Error('Decryption failed: ' + error.message);
}
}
// Hash passwords
async hashPassword(password, salt = null) {
salt = salt || crypto.randomBytes(this.saltLength);
return new Promise((resolve, reject) => {
crypto.pbkdf2(password, salt, this.iterations, this.keyLength, 'sha256', (err, derivedKey) => {
if (err) reject(err);
const result = Buffer.concat([salt, derivedKey]);
resolve(result.toString('base64'));
});
});
}
// Verify password
async verifyPassword(password, hashedPassword) {
try {
const buffer = Buffer.from(hashedPassword, 'base64');
const salt = buffer.subarray(0, this.saltLength);
const storedHash = buffer.subarray(this.saltLength);
return new Promise((resolve, reject) => {
crypto.pbkdf2(password, salt, this.iterations, this.keyLength, 'sha256', (err, derivedKey) => {
if (err) reject(err);
resolve(crypto.timingSafeEqual(storedHash, derivedKey));
});
});
} catch (error) {
return false;
}
}
// Generate secure random tokens
generateSecureToken(length = 32) {
return crypto.randomBytes(length).toString('hex');
}
}
module.exports = EncryptionService;
Security Middleware Templates
Input Validation and Sanitization
// middleware/security.js
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const validator = require('validator');
const xss = require('xss');
class SecurityMiddleware {
// Rate limiting
static createRateLimiter(options = {}) {
return rateLimit({
windowMs: options.windowMs || 15 * 60 * 1000, // 15 minutes
max: options.max || 100, // limit each IP to 100 requests per windowMs
message: {
error: {
code: 'RATE_LIMIT_EXCEEDED',
message: 'Too many requests from this IP'
}
},
standardHeaders: true,
legacyHeaders: false,
skip: options.skip || ((req) => {
// Skip rate limiting for health checks
return req.path === '/health';
})
});
}
// Input validation
static validateInput() {
return (req, res, next) => {
// Sanitize string inputs
const sanitizeObject = (obj) => {
if (typeof obj === 'string') {
return xss(obj.trim());
} else if (Array.isArray(obj)) {
return obj.map(sanitizeObject);
} else if (obj && typeof obj === 'object') {
const sanitized = {};
for (const [key, value] of Object.entries(obj)) {
sanitized[key] = sanitizeObject(value);
}
return sanitized;
}
return obj;
};
// Sanitize request body
if (req.body) {
req.body = sanitizeObject(req.body);
}
// Sanitize query parameters
if (req.query) {
req.query = sanitizeObject(req.query);
}
// Validate common fields
if (req.body.email && !validator.isEmail(req.body.email)) {
return res.status(400).json({
error: {
code: 'INVALID_EMAIL',
message: 'Invalid email format'
}
});
}
if (req.body.phone && !validator.isMobilePhone(req.body.phone)) {
return res.status(400).json({
error: {
code: 'INVALID_PHONE',
message: 'Invalid phone number format'
}
});
}
if (req.body.url && !validator.isURL(req.body.url)) {
return res.status(400).json({
error: {
code: 'INVALID_URL',
message: 'Invalid URL format'
}
});
}
next();
};
}
// Security headers
static securityHeaders() {
return helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
imgSrc: ["'self'", "data:", "https:"],
scriptSrc: ["'self'", "'unsafe-eval'"], // Be more restrictive in production
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
});
}
// CORS configuration
static corsConfig() {
return {
origin: (origin, callback) => {
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization'],
maxAge: 86400 // 24 hours
};
}
// Request logging
static requestLogger() {
return (req, res, next) => {
const startTime = Date.now();
const originalSend = res.send;
res.send = function(body) {
const duration = Date.now() - startTime;
// Log request details (consider using structured logging)
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
method: req.method,
url: req.url,
userAgent: req.get('User-Agent'),
ip: req.ip,
statusCode: res.statusCode,
duration: duration,
userId: req.user?.userId || 'anonymous'
}));
originalSend.call(this, body);
};
next();
};
}
}
module.exports = SecurityMiddleware;
Security Configuration Templates
Application Security Config
// config/security.js
module.exports = {
// JWT Configuration
jwt: {
accessTokenSecret: process.env.JWT_ACCESS_SECRET,
refreshTokenSecret: process.env.JWT_REFRESH_SECRET,
accessTokenExpiry: '15m',
refreshTokenExpiry: '7d',
issuer: 'myapp',
audience: 'myapp-users'
},
// Password Policy
password: {
minLength: 12,
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSpecialChars: true,
maxAttempts: 5,
lockoutDuration: 15 * 60 * 1000, // 15 minutes
historySize: 12 // Remember last 12 passwords
},
// Session Configuration
session: {
maxAge: 24 * 60 * 60 * 1000, // 24 hours
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
sameSite: 'strict'
},
// Rate Limiting
rateLimit: {
global: {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 1000 // requests per window
},
auth: {
windowMs: 15 * 60 * 1000,
max: 10 // login attempts per window
},
api: {
windowMs: 15 * 60 * 1000,
max: 100 // API calls per window
}
},
// File Upload Security
upload: {
maxSize: 10 * 1024 * 1024, // 10MB
allowedMimeTypes: [
'image/jpeg',
'image/png',
'image/gif',
'application/pdf',
'text/plain'
],
scanForViruses: true,
quarantinePath: '/tmp/quarantine'
},
// API Security
api: {
enableVersioning: true,
defaultVersion: 'v1',
enableDocumentation: process.env.NODE_ENV !== 'production',
maxRequestSize: '10mb',
enableRequestId: true
},
// HTTPS/TLS Configuration
tls: {
minVersion: 'TLSv1.2',
ciphers: [
'ECDHE-RSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES128-SHA256',
'ECDHE-RSA-AES256-SHA384'
].join(':'),
honorCipherOrder: true
},
// Content Security Policy
csp: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-eval'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
connectSrc: ["'self'"],
mediaSrc: ["'self'"],
objectSrc: ["'none'"],
childSrc: ["'none'"],
frameAncestors: ["'none'"],
formAction: ["'self'"],
baseUri: ["'self'"]
}
}
};
Database Security
-- database/security/security-setup.sql
-- Enable row level security
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
ALTER TABLE comments ENABLE ROW LEVEL SECURITY;
-- User access policies
CREATE POLICY users_own_data ON users
FOR ALL
TO authenticated_user
USING (id = current_user_id());
-- Content access policies
CREATE POLICY posts_owner_access ON posts
FOR ALL
TO authenticated_user
USING (author_id = current_user_id());
CREATE POLICY posts_public_read ON posts
FOR SELECT
TO authenticated_user
USING (status = 'published');
-- Comment access policies
CREATE POLICY comments_owner_access ON comments
FOR ALL
TO authenticated_user
USING (author_id = current_user_id());
-- Audit trail table
CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
table_name VARCHAR(64) NOT NULL,
operation VARCHAR(10) NOT NULL,
old_data JSONB,
new_data JSONB,
user_id INTEGER,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ip_address INET,
user_agent TEXT
);
-- Audit trigger function
CREATE OR REPLACE FUNCTION audit_trigger_function()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'DELETE' THEN
INSERT INTO audit_log (table_name, operation, old_data, user_id, ip_address)
VALUES (TG_TABLE_NAME, TG_OP, row_to_json(OLD),
current_setting('myapp.user_id', true)::INTEGER,
current_setting('myapp.ip_address', true)::INET);
RETURN OLD;
ELSIF TG_OP = 'UPDATE' THEN
INSERT INTO audit_log (table_name, operation, old_data, new_data, user_id, ip_address)
VALUES (TG_TABLE_NAME, TG_OP, row_to_json(OLD), row_to_json(NEW),
current_setting('myapp.user_id', true)::INTEGER,
current_setting('myapp.ip_address', true)::INET);
RETURN NEW;
ELSIF TG_OP = 'INSERT' THEN
INSERT INTO audit_log (table_name, operation, new_data, user_id, ip_address)
VALUES (TG_TABLE_NAME, TG_OP, row_to_json(NEW),
current_setting('myapp.user_id', true)::INTEGER,
current_setting('myapp.ip_address', true)::INET);
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql;
-- Apply audit triggers
CREATE TRIGGER users_audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE FUNCTION audit_trigger_function();
CREATE TRIGGER posts_audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON posts
FOR EACH ROW EXECUTE FUNCTION audit_trigger_function();
-- Database roles and permissions
CREATE ROLE app_readonly;
CREATE ROLE app_readwrite;
CREATE ROLE app_admin;
-- Grant permissions
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO app_readwrite;
GRANT DELETE ON posts, comments TO app_readwrite;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO app_admin;
-- Revoke dangerous permissions
REVOKE ALL ON schema public FROM public;
REVOKE CREATE ON schema public FROM public;
Compliance Templates
GDPR Compliance
// compliance/gdpr-service.js
class GDPRComplianceService {
constructor(dataService) {
this.dataService = dataService;
}
// Right to Access - Article 15
async generateDataExport(userId) {
const userData = await this.dataService.getAllUserData(userId);
return {
exportDate: new Date().toISOString(),
userId: userId,
personalData: {
profile: userData.profile,
preferences: userData.preferences,
activityLog: userData.activities,
communications: userData.communications
},
dataProcessingInfo: {
purposes: [
'Service provision',
'Account management',
'Communication',
'Analytics (anonymized)'
],
legalBasis: 'Consent and Contract',
retentionPeriod: '2 years after account closure',
thirdParties: ['Payment processor', 'Email service']
}
};
}
// Right to Rectification - Article 16
async updatePersonalData(userId, updates) {
// Validate updates
const validatedUpdates = this.validatePersonalDataUpdates(updates);
// Update data
const result = await this.dataService.updateUserData(userId, validatedUpdates);
// Log the rectification
await this.logDataProcessingActivity({
userId,
activity: 'data_rectification',
changes: validatedUpdates,
timestamp: new Date(),
legalBasis: 'Article 16 - Right to rectification'
});
return result;
}
// Right to Erasure - Article 17
async deleteUserData(userId, reason = 'user_request') {
const deletionPlan = {
immediate: [
'user_profile',
'preferences',
'session_data'
],
delayed: [ // Keep for legal/business reasons but anonymize
'transaction_history', // Keep for accounting (anonymized)
'support_tickets' // Keep for quality assurance (anonymized)
],
retain: [ // Legal obligation to retain
'audit_logs' // Legal/regulatory requirement
]
};
// Execute deletion plan
const deletionResults = {
deleted: [],
anonymized: [],
retained: []
};
// Immediate deletion
for (const table of deletionPlan.immediate) {
await this.dataService.deleteUserDataFromTable(userId, table);
deletionResults.deleted.push(table);
}
// Anonymization
for (const table of deletionPlan.delayed) {
await this.dataService.anonymizeUserDataInTable(userId, table);
deletionResults.anonymized.push(table);
}
// Retention with justification
for (const table of deletionPlan.retain) {
deletionResults.retained.push({
table,
reason: 'Legal obligation',
retentionPeriod: '7 years'
});
}
// Log the erasure
await this.logDataProcessingActivity({
userId,
activity: 'data_erasure',
reason: reason,
deletionResults,
timestamp: new Date(),
legalBasis: 'Article 17 - Right to erasure'
});
return deletionResults;
}
// Data Portability - Article 20
async generatePortableData(userId) {
const userData = await this.dataService.getPortableUserData(userId);
return {
format: 'JSON',
encoding: 'UTF-8',
exportDate: new Date().toISOString(),
data: {
profile: userData.profile,
content: userData.userGeneratedContent,
preferences: userData.preferences,
connections: userData.connections
},
metadata: {
dataController: 'MyApp Inc.',
contactEmail: 'privacy@myapp.com',
exportedBy: 'GDPR Data Portability Service',
version: '1.0'
}
};
}
// Consent Management
async updateConsent(userId, consentChanges) {
const currentConsent = await this.dataService.getUserConsent(userId);
const updatedConsent = {
...currentConsent,
...consentChanges,
lastUpdated: new Date(),
version: currentConsent.version + 1
};
await this.dataService.updateUserConsent(userId, updatedConsent);
// Log consent changes
await this.logDataProcessingActivity({
userId,
activity: 'consent_update',
changes: consentChanges,
timestamp: new Date(),
legalBasis: 'Article 6(1)(a) - Consent'
});
return updatedConsent;
}
async logDataProcessingActivity(activity) {
return await this.dataService.createAuditLog({
...activity,
regulation: 'GDPR',
compliance: true
});
}
}
module.exports = GDPRComplianceService;
Security Testing Templates
Security Test Suite
// tests/security.test.js
const request = require('supertest');
const app = require('../app');
describe('Security Tests', () => {
describe('Authentication', () => {
test('should reject requests without authentication', async () => {
const response = await request(app)
.get('/api/protected')
.expect(401);
expect(response.body.error.code).toBe('UNAUTHORIZED');
});
test('should reject invalid tokens', async () => {
const response = await request(app)
.get('/api/protected')
.set('Authorization', 'Bearer invalid-token')
.expect(401);
expect(response.body.error.code).toBe('INVALID_TOKEN');
});
test('should accept valid tokens', async () => {
const token = generateValidToken();
const response = await request(app)
.get('/api/protected')
.set('Authorization', `Bearer ${token}`)
.expect(200);
});
});
describe('Input Validation', () => {
test('should sanitize XSS attempts', async () => {
const maliciousInput = '<script>alert("xss")</script>';
const response = await request(app)
.post('/api/comments')
.send({ content: maliciousInput })
.expect(200);
expect(response.body.content).not.toContain('<script>');
});
test('should validate email format', async () => {
const response = await request(app)
.post('/api/users')
.send({ email: 'invalid-email' })
.expect(400);
expect(response.body.error.code).toBe('INVALID_EMAIL');
});
test('should enforce password policy', async () => {
const response = await request(app)
.post('/api/auth/register')
.send({
email: 'test@example.com',
password: '123' // Too weak
})
.expect(400);
expect(response.body.error.code).toBe('WEAK_PASSWORD');
});
});
describe('Rate Limiting', () => {
test('should enforce rate limits', async () => {
const requests = Array.from({ length: 101 }, (_, i) =>
request(app).get('/api/test')
);
const responses = await Promise.all(requests);
// Last request should be rate limited
expect(responses[100].status).toBe(429);
expect(responses[100].body.error.code).toBe('RATE_LIMIT_EXCEEDED');
});
});
describe('SQL Injection Prevention', () => {
test('should prevent SQL injection attacks', async () => {
const sqlInjection = "'; DROP TABLE users; --";
const response = await request(app)
.get('/api/users')
.query({ search: sqlInjection })
.expect(200);
// Verify database is intact
const userCount = await request(app)
.get('/api/users/count')
.expect(200);
expect(userCount.body.count).toBeGreaterThan(0);
});
});
describe('Access Control', () => {
test('should enforce role-based access control', async () => {
const userToken = generateUserToken();
const response = await request(app)
.delete('/api/admin/users/1')
.set('Authorization', `Bearer ${userToken}`)
.expect(403);
expect(response.body.error.code).toBe('FORBIDDEN');
});
test('should allow admin access', async () => {
const adminToken = generateAdminToken();
await request(app)
.get('/api/admin/dashboard')
.set('Authorization', `Bearer ${adminToken}`)
.expect(200);
});
});
});
Security Monitoring
Security Event Monitor
// monitoring/security-monitor.js
class SecurityMonitor {
constructor(alertService) {
this.alertService = alertService;
this.suspiciousPatterns = new Map();
this.setupMonitoring();
}
setupMonitoring() {
// Track failed login attempts
this.trackFailedLogins();
// Monitor suspicious API usage
this.trackAPIAnomalies();
// Watch for privilege escalation attempts
this.trackPrivilegeEscalation();
// Monitor data access patterns
this.trackDataAccess();
}
trackFailedLogins() {
setInterval(async () => {
const recentFailures = await this.getRecentFailedLogins();
// Check for brute force patterns
const ipFailures = new Map();
recentFailures.forEach(failure => {
const count = ipFailures.get(failure.ip) || 0;
ipFailures.set(failure.ip, count + 1);
});
// Alert on suspicious activity
for (const [ip, count] of ipFailures) {
if (count > 10) { // More than 10 failures in monitoring window
await this.alertService.sendAlert({
type: 'BRUTE_FORCE_ATTEMPT',
severity: 'HIGH',
ip: ip,
failureCount: count,
timeWindow: '5 minutes'
});
}
}
}, 5 * 60 * 1000); // Check every 5 minutes
}
trackAPIAnomalies() {
setInterval(async () => {
const recentRequests = await this.getRecentAPIRequests();
// Analyze request patterns
const userRequests = new Map();
recentRequests.forEach(request => {
const key = `${request.userId}:${request.endpoint}`;
const count = userRequests.get(key) || 0;
userRequests.set(key, count + 1);
});
// Check for unusual activity
for (const [key, count] of userRequests) {
const [userId, endpoint] = key.split(':');
if (count > 1000) { // Unusual high volume
await this.alertService.sendAlert({
type: 'API_ABUSE',
severity: 'MEDIUM',
userId: userId,
endpoint: endpoint,
requestCount: count
});
}
}
}, 10 * 60 * 1000); // Check every 10 minutes
}
async logSecurityEvent(event) {
const securityEvent = {
id: generateUniqueId(),
timestamp: new Date(),
type: event.type,
severity: event.severity || 'MEDIUM',
userId: event.userId,
ip: event.ip,
userAgent: event.userAgent,
details: event.details,
resolved: false
};
await this.storeSecurityEvent(securityEvent);
// Send immediate alerts for high severity events
if (event.severity === 'HIGH' || event.severity === 'CRITICAL') {
await this.alertService.sendImmediateAlert(securityEvent);
}
return securityEvent.id;
}
}
module.exports = SecurityMonitor;
These security templates provide comprehensive protection for modern applications. Customize them based on your specific security requirements and threat model. Always conduct security audits and penetration testing to validate your implementation.