การดำเนินงาน AI API ในระดับ Production ต้องให้ความสำคัญกับความปลอดภัยเป็นอันดับหนึ่ง ในบทความนี้ผมจะแบ่งปันประสบการณ์ตรงจากการทำ Security Audit สำหรับระบบ AI API ที่รองรับ LLM calls หลายหมื่นครั้งต่อวัน พร้อมแนะนำวิธีการที่ใช้ได้จริงในการปกป้องข้อมูลผู้ใช้และควบคุมการเข้าถึงอย่างมีประสิทธิภาพ

ทำไมต้อง Security Audit สำหรับ AI API

จากประสบการณ์ที่ผมเคยพบเหตุการณ์ที่ข้อมูล API key รั่วไหลผ่าน log file ที่ไม่ได้ sanitize ซึ่งส่งผลให้เกิดค่าใช้จ่ายที่ไม่คาดคิดหลายพันดอลลาร์ในเดือนเดียว การทำ Security Audit ที่ครอบคลุมจึงเป็นสิ่งจำเป็น โดยเฉพาะอย่างยิ่งสำหรับองค์กรที่ใช้ HolySheep AI ซึ่งมีอัตราค่าบริการที่คุ้มค่ามาก (¥1=$1 ประหยัดได้ถึง 85%+ เมื่อเทียบกับผู้ให้บริการรายอื่น) การป้องกันไม่ให้ API key ถูกขโมยจึงมีความสำคัญอย่างยิ่งต่อการควบคุมค่าใช้จ่าย

Log Desensitization: การปกปิดข้อมูลอ่อนไหวใน Log

ปัญหาหลักที่พบบ่อย

ในระบบ AI API มีข้อมูลหลายประเภทที่ต้องปกปิดใน log ได้แก่ API keys, user prompts ที่อาจมีข้อมูลส่วนบุคคล, response ที่มีข้อมูลละเอียดอ่อน และ token usage ที่อาจเปิดเผยรูปแบบการใช้งานของผู้ใช้ การไม่ปกปิดข้อมูลเหล่านี้ถือเป็นการละเมิด GDPR และ PDPA อย่างร้ายแรง

Middleware สำหรับ Log Sanitization

// middleware/logger-sanitizer.js
const SensitiveFields = {
  apiKey: ['api-key', 'x-api-key', 'authorization', 'Authorization', 'api_key'],
  personalData: ['ssn', 'passport', 'credit_card', 'phone', 'email', 'address', 'birthdate'],
  financial: ['balance', 'transaction_id', 'account_number', 'routing_number'],
  aiFields: ['input_tokens', 'output_tokens', 'usage', 'prompt', 'completion']
};

class LogSanitizer {
  constructor(options = {}) {
    this.replacementChar = options.replacementChar || '*';
    this.replacementPattern = options.replacementPattern || '[REDACTED]';
    this.maxVisibleChars = options.maxVisibleChars || 4;
    this.customRules = options.customRules || {};
  }

  sanitize(input, context = 'default') {
    if (input === null || input === undefined) {
      return input;
    }

    try {
      if (typeof input === 'string') {
        return this.sanitizeString(input);
      }
      
      if (typeof input === 'object') {
        return this.sanitizeObject(input);
      }

      return input;
    } catch (error) {
      return this.replacementPattern;
    }
  }

  sanitizeString(input) {
    // ปกปิด API Key patterns
    let result = input.replace(
      /(sk-[a-zA-Z0-9]{20,})|(holysheep-[a-zA-Z0-9]{32,})/gi,
      this.replacementPattern
    );

    // ปกปิด Bearer tokens
    result = result.replace(
      /Bearer\s+[a-zA-Z0-9._-]+/gi,
      Bearer ${this.replacementPattern}
    );

    // ปกปิด Email addresses
    result = result.replace(
      /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
      this.maskEmail.bind(this)
    );

    // ปกปิด Phone numbers
    result = result.replace(
      /(\+66|0)[0-9]{9,10}/g,
      this.maskPhone.bind(this)
    );

    return result;
  }

  maskEmail(email) {
    const [local, domain] = email.split('@');
    const maskedLocal = local.substring(0, this.maxVisibleChars) + 
                        this.replacementChar.repeat(Math.max(0, local.length - this.maxVisibleChars));
    return ${maskedLocal}@${domain};
  }

  maskPhone(phone) {
    return phone.substring(0, 3) + '*'.repeat(phone.length - 6) + phone.slice(-3);
  }

  sanitizeObject(obj, path = '') {
    const sanitized = {};

    for (const [key, value] of Object.entries(obj)) {
      const fullPath = path ? ${path}.${key} : key;
      const lowerKey = key.toLowerCase();

      // ตรวจสอบว่า field นี้เป็น sensitive field หรือไม่
      const isSensitive = this.isSensitiveField(lowerKey, fullPath);

      if (isSensitive) {
        sanitized[key] = this.replacementPattern;
        continue;
      }

      // Apply custom rules
      if (this.customRules[lowerKey]) {
        sanitized[key] = this.customRules[lowerKey](value);
        continue;
      }

      // Recursive sanitization
      if (typeof value === 'object' && value !== null) {
        sanitized[key] = Array.isArray(value) 
          ? value.map(item => this.sanitize(item, fullPath))
          : this.sanitizeObject(value, fullPath);
      } else {
        sanitized[key] = value;
      }
    }

    return sanitized;
  }

  isSensitiveField(key, fullPath) {
    const allSensitiveFields = [
      ...SensitiveFields.apiKey,
      ...SensitiveFields.personalData,
      ...SensitiveFields.financial,
      ...SensitiveFields.aiFields
    ];

    return allSensitiveFields.some(field => 
      key.includes(field.toLowerCase()) || fullPath.includes(field)
    );
  }
}

// Express middleware integration
const createSanitizedLogger = (sanitizer) => {
  return (req, res, next) => {
    const originalSend = res.send;
    const originalJson = res.json;
    const startTime = Date.now();

    // Sanitize request
    req.sanitizedBody = sanitizer.sanitize(req.body);
    req.sanitizedQuery = sanitizer.sanitize(req.query);
    req.sanitizedHeaders = sanitizer.sanitize(req.headers);

    // Override response methods
    res.json = function(body) {
      const sanitizedBody = sanitizer.sanitize(body);
      const duration = Date.now() - startTime;
      
      console.log(JSON.stringify({
        timestamp: new Date().toISOString(),
        method: req.method,
        path: req.path,
        statusCode: res.statusCode,
        duration: ${duration}ms,
        request: {
          body: req.sanitizedBody,
          query: req.sanitizedQuery
        },
        response: sanitizedBody
      }));

      return originalJson.call(this, sanitizedBody);
    };

    next();
  };
};

module.exports = { LogSanitizer, createSanitizedLogger, SensitiveFields };

การใช้งานร่วมกับ AI API Client

// config/ai-client.js
const OpenAI = require('openai');
const { LogSanitizer } = require('../middleware/logger-sanitizer');

class SecureAIClient {
  constructor() {
    this.sanitizer = new LogSanitizer({
      maxVisibleChars: 3,
      customRules: {
        'prompt': (value) => typeof value === 'string' 
          ? value.substring(0, 100) + '...' 
          : '[COMPLEX_PROMPT]',
        'messages': (messages) => messages.map(m => ({
          role: m.role,
          content: m.content ? m.content.substring(0, 100) + '...' : m.content
        }))
      }
    });
  }

  createClient() {
    return new OpenAI({
      apiKey: process.env.HOLYSHEEP_API_KEY, // YOUR_HOLYSHEEP_API_KEY
      baseURL: 'https://api.holysheep.ai/v1',
      defaultHeaders: {
        'X-Request-ID': this.generateRequestId(),
        'X-Client-Version': '1.0.0'
      },
      timeout: 60000,
      maxRetries: 3
    });
  }

  async chat(request) {
    const client = this.createClient();
    
    // Sanitize request before logging
    const sanitizedRequest = this.sanitizer.sanitize(request);
    
    console.log('AI Request:', JSON.stringify({
      timestamp: new Date().toISOString(),
      model: request.model,
      sanitizedRequest: sanitizedRequest
    }, null, 2));

    try {
      const response = await client.chat.completions.create({
        model: request.model,
        messages: request.messages,
        temperature: request.temperature,
        max_tokens: request.max_tokens,
        stream: request.stream || false
      });

      // Sanitize response before logging
      const sanitizedResponse = this.sanitizer.sanitize(response);
      
      console.log('AI Response:', JSON.stringify({
        timestamp: new Date().toISOString(),
        model: response.model,
        usage: {
          prompt_tokens: response.usage?.prompt_tokens,
          completion_tokens: response.usage?.completion_tokens,
          total_tokens: response.usage?.total_tokens
        },
        sanitizedResponse: sanitizedResponse
      }, null, 2));

      return response;
    } catch (error) {
      console.error('AI Error:', JSON.stringify({
        timestamp: new Date().toISOString(),
        error: {
          message: error.message,
          type: error.type,
          code: error.code
        },
        // อย่าลืม sanitize error details ด้วย
        sanitizedError: this.sanitizer.sanitize(error)
      }, null, 2));
      
      throw error;
    }
  }

  generateRequestId() {
    return req_${Date.now()}_${Math.random().toString(36).substring(2, 15)};
  }
}

module.exports = new SecureAIClient();

// Usage example:
// const response = await secureAIClient.chat({
//   model: 'gpt-4o',
//   messages: [
//     { role: 'system', content: 'You are a helpful assistant.' },
//     { role: 'user', content: 'ช่วยสรุปข้อมูลลูกค้าหมายเลข 12345' }
//   ],
//   temperature: 0.7,
//   max_tokens: 1000
// });

Access Control: ระบบควบคุมการเข้าถึงแบบหลายชั้น

Rate Limiting และ Quota Management

// middleware/access-control.js
const rateLimit = require('express-rate-limit');
const Redis = require('ioredis');

// Redis client for distributed rate limiting
const redis = new Redis(process.env.REDIS_URL);

// Tier-based rate limits
const RATE_LIMITS = {
  free: {
    requestsPerMinute: 10,
    requestsPerDay: 100,
    tokensPerMonth: 100000,
    maxConcurrent: 1
  },
  pro: {
    requestsPerMinute: 60,
    requestsPerDay: 5000,
    tokensPerMonth: 10000000,
    maxConcurrent: 5
  },
  enterprise: {
    requestsPerMinute: 600,
    requestsPerDay: 100000,
    tokensPerMonth: -1, // unlimited
    maxConcurrent: 20
  }
};

// Middleware สำหรับตรวจสอบ Rate Limit
const createRateLimiter = (tier = 'free') => {
  const limits = RATE_LIMITS[tier];

  return async (req, res, next) => {
    const userId = req.user?.id || req.apiKey?.key_id;
    const clientIp = req.ip;
    const key = ratelimit:${tier}:${userId || clientIp};

    try {
      const now = Date.now();
      const windowMs = 60 * 1000; // 1 minute window

      // ใช้ Redis sorted set สำหรับ sliding window rate limit
      const multi = redis.multi();
      multi.zremrangebyscore(key, 0, now - windowMs);
      multi.zadd(key, now, ${now}-${Math.random()});
      multi.zcard(key);
      multi.expire(key, 120);

      const results = await multi.exec();
      const requestCount = results[2][1];

      // Set rate limit headers
      res.set({
        'X-RateLimit-Limit': limits.requestsPerMinute,
        'X-RateLimit-Remaining': Math.max(0, limits.requestsPerMinute - requestCount),
        'X-RateLimit-Reset': new Date(now + windowMs).toISOString(),
        'X-RateLimit-Tier': tier
      });

      if (requestCount > limits.requestsPerMinute) {
        return res.status(429).json({
          error: {
            type: 'rate_limit_exceeded',
            message: 'คำขอมากเกินกว่าที่กำหนดไว้สำหรับแพลนของคุณ',
            retryAfter: Math.ceil(windowMs / 1000)
          }
        });
      }

      // ตรวจสอบ daily limit
      const dailyKey = ratelimit:${tier}:daily:${userId || clientIp};
      const dailyCount = await redis.get(dailyKey);
      
      if (dailyCount && parseInt(dailyCount) >= limits.requestsPerDay) {
        return res.status(429).json({
          error: {
            type: 'daily_limit_exceeded',
            message: 'คุณใช้งานครบตามโควต้ารายวันแล้ว กรุณาลองใหม่พรุ่งนี้'
          }
        });
      }

      // Increment daily counter
      await redis.incr(dailyKey);
      await redis.expire(dailyKey, 86400); // 24 hours

      next();
    } catch (error) {
      console.error('Rate limit check failed:', error);
      // Fail open - allow request but log warning
      next();
    }
  };
};

// API Key validation middleware
const validateAPIKey = async (req, res, next) => {
  const apiKey = req.headers['x-api-key'] || req.headers['authorization']?.replace('Bearer ', '');

  if (!apiKey) {
    return res.status(401).json({
      error: {
        type: 'missing_api_key',
        message: 'กรุณาระบุ API Key'
      }
    });
  }

  try {
    // Validate against database
    const keyRecord = await validateKeyInDatabase(apiKey);
    
    if (!keyRecord) {
      return res.status(401).json({
        error: {
          type: 'invalid_api_key',
          message: 'API Key ไม่ถูกต้อง'
        }
      });
    }

    // Check if key is active
    if (keyRecord.status !== 'active') {
      return res.status(403).json({
        error: {
          type: 'inactive_api_key',
          message: 'API Key นี้ถูกระงับการใช้งาน'
        }
      });
    }

    // Attach key info to request
    req.apiKey = keyRecord;
    req.user = {
      id: keyRecord.user_id,
      tier: keyRecord.tier,
      permissions: keyRecord.permissions
    };

    next();
  } catch (error) {
    console.error('API Key validation error:', error);
    return res.status(500).json({
      error: {
        type: 'internal_error',
        message: 'เ