セキュリティベストプラクティス
この包括的なガイドでは、Traeで構築されたアプリケーションのセキュリティベストプラクティス、脅威軽減、安全な開発プラクティスについて説明します。
概要
セキュリティはアプリケーション開発の重要な側面です。このガイドでは以下について説明します:
- セキュリティの基礎
- 認証と認可
- データ保護
- ネットワークセキュリティ
- 安全なコーディングプラクティス
- 脆弱性管理
- コンプライアンスと監査
セキュリティの基礎
セキュリティ原則
CIA三要素
- 機密性: 機密情報を不正アクセスから保護
- 完全性: データの正確性を確保し、不正な変更を防止
- 可用性: システムのアクセス性と機能性を維持
多層防御
- 複数のセキュリティ層: 複数のレベルでセキュリティを実装
- フェイルセーフデフォルト: 安全な設定をデフォルトとする
- 最小権限の原則: 必要最小限の権限を付与
- ゼロトラスト: 信頼せず、常に検証
脅威モデリング
javascript
// security/threat-model.js
const threatModel = {
assets: [
'ユーザー認証情報',
'個人データ',
'支払い情報',
'ビジネスロジック',
'インフラストラクチャ'
],
threats: [
'SQLインジェクション',
'クロスサイトスクリプティング(XSS)',
'クロスサイトリクエストフォージェリ(CSRF)',
'認証バイパス',
'データ侵害',
'DDoS攻撃'
],
vulnerabilities: [
'未検証の入力',
'弱い認証',
'安全でない直接オブジェクト参照',
'セキュリティ設定ミス',
'機密データの露出'
],
mitigations: [
'入力検証',
'出力エンコーディング',
'認証メカニズム',
'認可制御',
'暗号化',
'セキュリティヘッダー'
]
};認証と認可
JWT Authentication
javascript
// auth/jwt-auth.js
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const rateLimit = require('express-rate-limit');
class AuthService {
constructor() {
this.jwtSecret = process.env.JWT_SECRET;
this.jwtExpiry = process.env.JWT_EXPIRY || '1h';
this.refreshTokenExpiry = process.env.REFRESH_TOKEN_EXPIRY || '7d';
}
// セキュアなパスワードハッシュ化
async hashPassword(password) {
const saltRounds = 12;
return await bcrypt.hash(password, saltRounds);
}
// タイミング攻撃保護付きパスワード検証
async verifyPassword(password, hash) {
return await bcrypt.compare(password, hash);
}
// セキュアなJWTトークン生成
generateTokens(user) {
const payload = {
id: user.id,
email: user.email,
role: user.role,
iat: Math.floor(Date.now() / 1000)
};
const accessToken = jwt.sign(payload, this.jwtSecret, {
expiresIn: this.jwtExpiry,
issuer: 'trae-app',
audience: 'trae-users'
});
const refreshToken = jwt.sign(
{ id: user.id, type: 'refresh' },
this.jwtSecret,
{ expiresIn: this.refreshTokenExpiry }
);
return { accessToken, refreshToken };
}
// JWTトークンの検証とデコード
verifyToken(token) {
try {
return jwt.verify(token, this.jwtSecret, {
issuer: 'trae-app',
audience: 'trae-users'
});
} catch (error) {
throw new Error('無効なトークン');
}
}
}
// 認証エンドポイントのレート制限
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分
max: 5, // ウィンドウあたり5回の試行
message: '認証試行回数が多すぎます',
standardHeaders: true,
legacyHeaders: false
});
module.exports = { AuthService, authLimiter };Role-Based Access Control (RBAC)
javascript
// auth/rbac.js
class RBACService {
constructor() {
this.permissions = new Map();
this.roles = new Map();
this.initializeRoles();
}
initializeRoles() {
// 権限を定義
const permissions = {
'user:read': 'ユーザー情報の読み取り',
'user:write': 'ユーザー情報の変更',
'user:delete': 'ユーザーの削除',
'post:read': '投稿の読み取り',
'post:write': '投稿の作成/編集',
'post:delete': '投稿の削除',
'admin:access': '管理パネルへのアクセス'
};
// 権限付きロールを定義
const roles = {
'guest': ['post:read'],
'user': ['post:read', 'post:write', 'user:read'],
'moderator': ['post:read', 'post:write', 'post:delete', 'user:read'],
'admin': Object.keys(permissions)
};
Object.entries(permissions).forEach(([key, value]) => {
this.permissions.set(key, value);
});
Object.entries(roles).forEach(([role, perms]) => {
this.roles.set(role, perms);
});
}
hasPermission(userRole, permission) {
const rolePermissions = this.roles.get(userRole);
return rolePermissions && rolePermissions.includes(permission);
}
// Middleware for permission checking
requirePermission(permission) {
return (req, res, next) => {
const user = req.user;
if (!user) {
return res.status(401).json({ error: '認証が必要です' });
}
if (!this.hasPermission(user.role, permission)) {
return res.status(403).json({ error: '権限が不足しています' });
}
next();
};
}
}
module.exports = new RBACService();Multi-Factor Authentication (MFA)
javascript
// auth/mfa.js
const speakeasy = require('speakeasy');
const QRCode = require('qrcode');
class MFAService {
// ユーザー用TOTPシークレット生成
generateSecret(userEmail) {
const secret = speakeasy.generateSecret({
name: `Trae App (${userEmail})`,
issuer: 'Trae',
length: 32
});
return {
secret: secret.base32,
qrCode: secret.otpauth_url
};
}
// TOTP設定用QRコード生成
async generateQRCode(otpauthUrl) {
try {
return await QRCode.toDataURL(otpauthUrl);
} catch (error) {
throw new Error('QRコードの生成に失敗しました');
}
}
// TOTPトークン検証
verifyToken(token, secret) {
return speakeasy.totp.verify({
secret: secret,
encoding: 'base32',
token: token,
window: 2 // 2タイムステップの許容範囲
});
}
// バックアップコード生成
generateBackupCodes(count = 10) {
const codes = [];
for (let i = 0; i < count; i++) {
codes.push(this.generateRandomCode());
}
return codes;
}
generateRandomCode() {
return Math.random().toString(36).substring(2, 10).toUpperCase();
}
}
module.exports = new MFAService();Data Protection
Encryption
javascript
// security/encryption.js
const crypto = require('crypto');
class EncryptionService {
constructor() {
this.algorithm = 'aes-256-gcm';
this.keyLength = 32;
this.ivLength = 16;
this.tagLength = 16;
}
// パスワードから暗号化キーを生成
deriveKey(password, salt) {
return crypto.pbkdf2Sync(password, salt, 100000, this.keyLength, 'sha256');
}
// 機密データの暗号化
encrypt(text, key) {
const iv = crypto.randomBytes(this.ivLength);
const cipher = crypto.createCipher(this.algorithm, key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const tag = cipher.getAuthTag();
return {
encrypted,
iv: iv.toString('hex'),
tag: tag.toString('hex')
};
}
// 機密データの復号化
decrypt(encryptedData, key) {
const { encrypted, iv, tag } = encryptedData;
const decipher = crypto.createDecipher(
this.algorithm,
key,
Buffer.from(iv, 'hex')
);
decipher.setAuthTag(Buffer.from(tag, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// 機密データのハッシュ化(一方向)
hash(data, salt) {
return crypto.pbkdf2Sync(data, salt, 100000, 64, 'sha256').toString('hex');
}
// セキュアなランダムソルトの生成
generateSalt() {
return crypto.randomBytes(32).toString('hex');
}
}
module.exports = new EncryptionService();Data Sanitization
javascript
// security/sanitization.js
const DOMPurify = require('isomorphic-dompurify');
const validator = require('validator');
class DataSanitizer {
// HTMLコンテンツのサニタイズ
sanitizeHTML(html) {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'ol', 'ul', 'li'],
ALLOWED_ATTR: ['href', 'title'],
FORBID_SCRIPT: true,
FORBID_TAGS: ['script', 'object', 'embed', 'link', 'style']
});
}
// ユーザー入力のサニタイズ
sanitizeInput(input) {
if (typeof input !== 'string') {
return input;
}
// ヌルバイトを削除
input = input.replace(/\0/g, '');
// HTMLエンティティをエスケープ
input = validator.escape(input);
// 空白文字をトリム
input = input.trim();
return input;
}
// メールアドレスの検証とサニタイズ
sanitizeEmail(email) {
if (!validator.isEmail(email)) {
throw new Error('無効なメールアドレス形式');
}
return validator.normalizeEmail(email);
}
// URLの検証とサニタイズ
sanitizeURL(url) {
if (!validator.isURL(url, {
protocols: ['http', 'https'],
require_protocol: true
})) {
throw new Error('無効なURL形式');
}
return url;
}
// SQL入力のサニタイズ(パラメータ化クエリと併用)
sanitizeSQL(input) {
// SQLインジェクションパターンを削除
const sqlPatterns = [
/('|(\-\-)|(;)|(\||\|)|(\*|\*))/i,
/(exec(\s|\+)+(s|x)p\w+)/i,
/union.*select/i,
/insert.*into/i,
/delete.*from/i,
/update.*set/i,
/drop.*table/i
];
for (const pattern of sqlPatterns) {
if (pattern.test(input)) {
throw new Error('悪意のあるSQLが検出されました');
}
}
return input;
}
}
module.exports = new DataSanitizer();Network Security
Security Headers
javascript
// security/headers.js
const helmet = require('helmet');
function securityHeaders(app) {
// 基本的なセキュリティヘッダーにHelmetを使用
app.use(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'"],
connectSrc: ["'self'"],
frameSrc: ["'none'"],
objectSrc: ["'none'"],
upgradeInsecureRequests: []
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
// カスタムセキュリティヘッダー
app.use((req, res, next) => {
// クリックジャッキング防止
res.setHeader('X-Frame-Options', 'DENY');
// MIMEタイプスニッフィング防止
res.setHeader('X-Content-Type-Options', 'nosniff');
// XSS保護を有効化
res.setHeader('X-XSS-Protection', '1; mode=block');
// リファラーポリシー
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
// 権限ポリシー
res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
next();
});
}
module.exports = securityHeaders;CORS Configuration
javascript
// security/cors.js
const cors = require('cors');
const corsOptions = {
origin: function (origin, callback) {
const allowedOrigins = [
'https://yourdomain.com',
'https://www.yourdomain.com',
'https://app.yourdomain.com'
];
// オリジンなしのリクエストを許可(モバイルアプリなど)
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('CORSにより許可されていません'));
}
},
credentials: true,
optionsSuccessStatus: 200,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: [
'Origin',
'X-Requested-With',
'Content-Type',
'Accept',
'Authorization',
'X-CSRF-Token'
],
exposedHeaders: ['X-Total-Count'],
maxAge: 86400 // 24時間
};
module.exports = cors(corsOptions);HTTPS Enforcement
javascript
// security/https.js
function enforceHTTPS(req, res, next) {
// Check if request is secure
if (!req.secure && req.get('x-forwarded-proto') !== 'https') {
// Redirect to HTTPS
return res.redirect(301, `https://${req.get('host')}${req.url}`);
}
next();
}
// SSL/TLS configuration for Express
const https = require('https');
const fs = require('fs');
const sslOptions = {
key: fs.readFileSync('path/to/private-key.pem'),
cert: fs.readFileSync('path/to/certificate.pem'),
// Additional security options
secureProtocol: 'TLSv1_2_method',
ciphers: [
'ECDHE-RSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES128-SHA256',
'ECDHE-RSA-AES256-SHA384'
].join(':'),
honorCipherOrder: true
};
module.exports = { enforceHTTPS, sslOptions };Secure Coding Practices
Input Validation
javascript
// validation/input-validator.js
const Joi = require('joi');
class InputValidator {
// User registration validation
validateUserRegistration(data) {
const schema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string()
.min(8)
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/)
.required()
.messages({
'string.pattern.base': 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character'
}),
firstName: Joi.string().alphanum().min(2).max(50).required(),
lastName: Joi.string().alphanum().min(2).max(50).required(),
age: Joi.number().integer().min(13).max(120).optional()
});
return schema.validate(data);
}
// API input validation middleware
validateInput(schema) {
return (req, res, next) => {
const { error, value } = schema.validate(req.body);
if (error) {
return res.status(400).json({
error: 'Validation failed',
details: error.details.map(detail => detail.message)
});
}
req.validatedData = value;
next();
};
}
// File upload validation
validateFileUpload(file) {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
const maxSize = 5 * 1024 * 1024; // 5MB
if (!allowedTypes.includes(file.mimetype)) {
throw new Error('Invalid file type');
}
if (file.size > maxSize) {
throw new Error('File too large');
}
// Check for malicious file content
if (this.containsMaliciousContent(file.buffer)) {
throw new Error('Malicious file detected');
}
return true;
}
containsMaliciousContent(buffer) {
// Check for script tags, PHP code, etc.
const maliciousPatterns = [
/<script[^>]*>.*?<\/script>/gi,
/<\?php.*?\?>/gi,
/<%.*?%>/gi
];
const content = buffer.toString();
return maliciousPatterns.some(pattern => pattern.test(content));
}
}
module.exports = new InputValidator();SQL Injection Prevention
javascript
// database/secure-queries.js
const { Pool } = require('pg');
class SecureDatabase {
constructor() {
this.pool = new Pool({
// Connection configuration
});
}
// Always use parameterized queries
async getUserById(id) {
// ✅ Safe - parameterized query
const query = 'SELECT * FROM users WHERE id = $1';
const result = await this.pool.query(query, [id]);
return result.rows[0];
}
// ❌ Never do this - vulnerable to SQL injection
async unsafeGetUser(id) {
const query = `SELECT * FROM users WHERE id = ${id}`;
return await this.pool.query(query);
}
// Safe dynamic query building
async searchUsers(filters) {
let query = 'SELECT * FROM users WHERE 1=1';
const params = [];
let paramIndex = 1;
if (filters.name) {
query += ` AND name ILIKE $${paramIndex}`;
params.push(`%${filters.name}%`);
paramIndex++;
}
if (filters.email) {
query += ` AND email = $${paramIndex}`;
params.push(filters.email);
paramIndex++;
}
if (filters.status) {
query += ` AND status = $${paramIndex}`;
params.push(filters.status);
paramIndex++;
}
const result = await this.pool.query(query, params);
return result.rows;
}
// Stored procedures for complex operations
async createUserSecurely(userData) {
const query = 'CALL create_user_secure($1, $2, $3, $4)';
const params = [
userData.email,
userData.hashedPassword,
userData.firstName,
userData.lastName
];
return await this.pool.query(query, params);
}
}
module.exports = new SecureDatabase();Vulnerability Management
Security Scanning
javascript
// security/vulnerability-scanner.js
const { execSync } = require('child_process');
const fs = require('fs');
class VulnerabilityScanner {
// Scan dependencies for known vulnerabilities
async scanDependencies() {
try {
const auditResult = execSync('npm audit --json', { encoding: 'utf8' });
const audit = JSON.parse(auditResult);
return {
vulnerabilities: audit.vulnerabilities,
summary: audit.metadata
};
} catch (error) {
console.error('Dependency scan failed:', error.message);
return null;
}
}
// Check for security misconfigurations
checkSecurityConfig() {
const issues = [];
// Check environment variables
if (!process.env.JWT_SECRET || process.env.JWT_SECRET.length < 32) {
issues.push('JWT secret is too short or missing');
}
if (process.env.NODE_ENV === 'production' && process.env.DEBUG === 'true') {
issues.push('Debug mode enabled in production');
}
// Check file permissions
try {
const stats = fs.statSync('.env');
if (stats.mode & parseInt('077', 8)) {
issues.push('.env file has overly permissive permissions');
}
} catch (error) {
// .env file doesn't exist or can't be accessed
}
return issues;
}
// Generate security report
async generateSecurityReport() {
const report = {
timestamp: new Date().toISOString(),
dependencies: await this.scanDependencies(),
configuration: this.checkSecurityConfig(),
recommendations: this.getSecurityRecommendations()
};
return report;
}
getSecurityRecommendations() {
return [
'Regularly update dependencies',
'Use strong, unique passwords',
'Enable two-factor authentication',
'Implement proper logging and monitoring',
'Regular security audits',
'Keep security patches up to date'
];
}
}
module.exports = new VulnerabilityScanner();Security Monitoring
javascript
// security/security-monitor.js
const EventEmitter = require('events');
class SecurityMonitor extends EventEmitter {
constructor() {
super();
this.suspiciousActivities = new Map();
this.rateLimits = new Map();
}
// Monitor for suspicious login attempts
trackLoginAttempt(ip, success, userAgent) {
const key = `login:${ip}`;
const attempts = this.suspiciousActivities.get(key) || [];
attempts.push({
timestamp: Date.now(),
success,
userAgent
});
// Keep only last 10 attempts
if (attempts.length > 10) {
attempts.shift();
}
this.suspiciousActivities.set(key, attempts);
// Check for suspicious patterns
this.analyzeLoginPattern(ip, attempts);
}
analyzeLoginPattern(ip, attempts) {
const recentAttempts = attempts.filter(
attempt => Date.now() - attempt.timestamp < 15 * 60 * 1000 // 15 minutes
);
const failedAttempts = recentAttempts.filter(attempt => !attempt.success);
// Too many failed attempts
if (failedAttempts.length >= 5) {
this.emit('suspicious-activity', {
type: 'brute-force',
ip,
attempts: failedAttempts.length,
timeframe: '15 minutes'
});
}
// Different user agents from same IP
const userAgents = new Set(recentAttempts.map(a => a.userAgent));
if (userAgents.size > 3) {
this.emit('suspicious-activity', {
type: 'multiple-user-agents',
ip,
userAgents: Array.from(userAgents)
});
}
}
// Monitor API usage patterns
trackAPIUsage(ip, endpoint, responseTime) {
const key = `api:${ip}`;
const usage = this.rateLimits.get(key) || { requests: [], totalRequests: 0 };
usage.requests.push({
endpoint,
timestamp: Date.now(),
responseTime
});
usage.totalRequests++;
// Keep only last hour of requests
usage.requests = usage.requests.filter(
req => Date.now() - req.timestamp < 60 * 60 * 1000
);
this.rateLimits.set(key, usage);
// Check for API abuse
if (usage.requests.length > 1000) { // More than 1000 requests per hour
this.emit('suspicious-activity', {
type: 'api-abuse',
ip,
requestCount: usage.requests.length,
timeframe: '1 hour'
});
}
}
// Security event handler
handleSecurityEvent(event) {
console.warn('Security Alert:', event);
// Log to security monitoring system
this.logSecurityEvent(event);
// Take automated action if necessary
this.takeSecurityAction(event);
}
logSecurityEvent(event) {
// Log to external security monitoring service
// Implementation depends on your monitoring solution
}
takeSecurityAction(event) {
switch (event.type) {
case 'brute-force':
// Temporarily block IP
this.blockIP(event.ip, 30 * 60 * 1000); // 30 minutes
break;
case 'api-abuse':
// Rate limit IP
this.rateLimit(event.ip, 60 * 60 * 1000); // 1 hour
break;
}
}
blockIP(ip, duration) {
// Implementation depends on your infrastructure
console.log(`Blocking IP ${ip} for ${duration}ms`);
}
rateLimit(ip, duration) {
// Implementation depends on your infrastructure
console.log(`Rate limiting IP ${ip} for ${duration}ms`);
}
}
module.exports = new SecurityMonitor();Compliance and Auditing
GDPR Compliance
javascript
// compliance/gdpr.js
class GDPRCompliance {
// Data processing consent management
async recordConsent(userId, consentType, granted) {
const consent = {
userId,
consentType,
granted,
timestamp: new Date(),
ipAddress: req.ip,
userAgent: req.get('User-Agent')
};
// Store consent record
await this.storeConsentRecord(consent);
return consent;
}
// Right to access - export user data
async exportUserData(userId) {
const userData = {
profile: await this.getUserProfile(userId),
posts: await this.getUserPosts(userId),
comments: await this.getUserComments(userId),
loginHistory: await this.getLoginHistory(userId),
consents: await this.getConsentHistory(userId)
};
return {
exportDate: new Date(),
userId,
data: userData
};
}
// Right to erasure - delete user data
async deleteUserData(userId, retentionReason = null) {
const deletionLog = {
userId,
deletionDate: new Date(),
retentionReason,
deletedData: []
};
// Delete or anonymize user data
await this.anonymizeUserProfile(userId);
deletionLog.deletedData.push('profile');
await this.deleteUserPosts(userId);
deletionLog.deletedData.push('posts');
await this.deleteUserComments(userId);
deletionLog.deletedData.push('comments');
// Keep audit logs for compliance
await this.storeDeletionLog(deletionLog);
return deletionLog;
}
// Data portability
async generateDataExport(userId, format = 'json') {
const userData = await this.exportUserData(userId);
switch (format) {
case 'json':
return JSON.stringify(userData, null, 2);
case 'csv':
return this.convertToCSV(userData);
case 'xml':
return this.convertToXML(userData);
default:
throw new Error('Unsupported export format');
}
}
}
module.exports = new GDPRCompliance();Audit Logging
javascript
// audit/audit-logger.js
class AuditLogger {
constructor() {
this.auditEvents = [];
}
// Log security-relevant events
logEvent(event) {
const auditEvent = {
id: this.generateEventId(),
timestamp: new Date(),
type: event.type,
actor: event.actor,
resource: event.resource,
action: event.action,
outcome: event.outcome,
ipAddress: event.ipAddress,
userAgent: event.userAgent,
details: event.details
};
this.auditEvents.push(auditEvent);
this.persistAuditEvent(auditEvent);
return auditEvent.id;
}
// Middleware for automatic audit logging
auditMiddleware() {
return (req, res, next) => {
const originalSend = res.send;
res.send = function(data) {
// Log the request/response
this.logEvent({
type: 'api-request',
actor: req.user?.id || 'anonymous',
resource: req.path,
action: req.method,
outcome: res.statusCode < 400 ? 'success' : 'failure',
ipAddress: req.ip,
userAgent: req.get('User-Agent'),
details: {
query: req.query,
body: this.sanitizeRequestBody(req.body),
responseCode: res.statusCode
}
});
originalSend.call(this, data);
}.bind(this);
next();
};
}
sanitizeRequestBody(body) {
// Remove sensitive data from audit logs
const sanitized = { ...body };
delete sanitized.password;
delete sanitized.token;
delete sanitized.secret;
return sanitized;
}
generateEventId() {
return `audit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
async persistAuditEvent(event) {
// Store in database or external audit system
// Implementation depends on your audit storage solution
}
}
module.exports = new AuditLogger();Security Testing
Automated Security Tests
javascript
// 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).toBe('Authentication required');
});
test('should reject invalid JWT tokens', async () => {
const response = await request(app)
.get('/api/protected')
.set('Authorization', 'Bearer invalid-token')
.expect(401);
});
});
describe('Input Validation', () => {
test('should reject SQL injection attempts', async () => {
const maliciousInput = "'; DROP TABLE users; --";
const response = await request(app)
.post('/api/users')
.send({ name: maliciousInput })
.expect(400);
});
test('should reject XSS attempts', async () => {
const xssPayload = '<script>alert("xss")</script>';
const response = await request(app)
.post('/api/posts')
.send({ content: xssPayload })
.expect(400);
});
});
describe('Rate Limiting', () => {
test('should enforce rate limits', async () => {
// Make multiple requests quickly
const promises = Array(10).fill().map(() =>
request(app).post('/api/login').send({
email: 'test@example.com',
password: 'wrongpassword'
})
);
const responses = await Promise.all(promises);
const rateLimited = responses.some(res => res.status === 429);
expect(rateLimited).toBe(true);
});
});
});Best Practices Summary
Development
- Secure by Default: Use secure configurations as defaults
- Input Validation: Validate and sanitize all user input
- Output Encoding: Encode output to prevent XSS
- Parameterized Queries: Use parameterized queries to prevent SQL injection
- Error Handling: Don't expose sensitive information in error messages
Authentication & Authorization
- Strong Passwords: Enforce strong password policies
- Multi-Factor Authentication: Implement MFA for sensitive accounts
- Session Management: Use secure session management practices
- Principle of Least Privilege: Grant minimal necessary permissions
- Regular Access Reviews: Regularly review and update access permissions
Data Protection
- Encryption: Encrypt sensitive data at rest and in transit
- Key Management: Use proper key management practices
- Data Classification: Classify and handle data according to sensitivity
- Backup Security: Secure backup data and test recovery procedures
- Data Retention: Implement appropriate data retention policies
Infrastructure
- Network Segmentation: Segment networks to limit attack surface
- Regular Updates: Keep systems and dependencies updated
- Monitoring: Implement comprehensive security monitoring
- Incident Response: Have an incident response plan ready
- Regular Audits: Conduct regular security audits and penetration testing