Skip to content

セキュリティベストプラクティス

この包括的なガイドでは、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

  1. Secure by Default: Use secure configurations as defaults
  2. Input Validation: Validate and sanitize all user input
  3. Output Encoding: Encode output to prevent XSS
  4. Parameterized Queries: Use parameterized queries to prevent SQL injection
  5. Error Handling: Don't expose sensitive information in error messages

Authentication & Authorization

  1. Strong Passwords: Enforce strong password policies
  2. Multi-Factor Authentication: Implement MFA for sensitive accounts
  3. Session Management: Use secure session management practices
  4. Principle of Least Privilege: Grant minimal necessary permissions
  5. Regular Access Reviews: Regularly review and update access permissions

Data Protection

  1. Encryption: Encrypt sensitive data at rest and in transit
  2. Key Management: Use proper key management practices
  3. Data Classification: Classify and handle data according to sensitivity
  4. Backup Security: Secure backup data and test recovery procedures
  5. Data Retention: Implement appropriate data retention policies

Infrastructure

  1. Network Segmentation: Segment networks to limit attack surface
  2. Regular Updates: Keep systems and dependencies updated
  3. Monitoring: Implement comprehensive security monitoring
  4. Incident Response: Have an incident response plan ready
  5. Regular Audits: Conduct regular security audits and penetration testing

究極の AI 駆動 IDE 学習ガイド