ในยุคที่ข้อมูลคือสินทรัพย์สำคัญ การจัดการ Log จาก API Gateway อย่างถูกต้องตามกฎหมายกลายเป็นสิ่งจำเป็นอย่างยิ่ง โดยเฉพาะกฎหมาย PDPA ของไทยที่กำหนดให้ข้อมูลส่วนบุคคลต้องได้รับการคุ้มครองตั้งแต่การเก็บรวบรวม การประมวลผล ไปจนถึงการจัดเก็บ บทความนี้จะแนะนำวิธีการตั้งค่า Log บน HolySheep AI API Gateway ให้ปฏิบัติตามกฎหมาย ใช้งานได้จริง พร้อมโค้ดตัวอย่างที่รันได้ทันที
ทำไมต้องทำ Log ให้เป็นความลับ (Log Anonymization)
เมื่อผู้ใช้งานส่งคำขอผ่าน API Gateway โดยปกติ Log จะเก็บข้อมูลต่อไปนี้
- ที่อยู่ IP — ข้อมูลส่วนบุคคลที่สามารถระบุตัวตนได้
- User-Agent และ Cookie — อาจมี Session ID หรือข้อมูลติดตาม
- Request Body — อาจมีข้อมูลชื่อ นามสกุล อีเมล หมายเลขโทรศัพท์
- Response Body — อาจมีข้อมูลที่ตอบกลับผู้ใช้
- API Key และ Token — ข้อมูลความปลอดภัยที่ต้องซ่อน
หากไม่ทำ Log ให้เป็นความลับ องค์กรอาจเผชิญความเสี่ยงด้านกฎหมาย โดนปรับตาม PDPA สูงสุด 5 ล้านบาท หรือถูกฟ้องร้องจากลูกค้าที่ข้อมูลรั่วไหล นอกจากนี้ยังเป็นการละเมิด GDPR หากมีลูกค้าจากสหภาพยุโรปอีกด้วย
การตั้งค่า HolySheep API Gateway สำหรับ Log ที่ปลอดภัย
ขั้นตอนที่ 1: สร้าง Middleware สำหรับ Anonymization
สร้าง Middleware ที่จะควบคุมการทำ Log โดยกรองข้อมูลที่เป็นความลับออกก่อนเก็บลง Log Storage
// anonymize-middleware.js
const crypto = require('crypto');
// รายการฟิลด์ที่ต้องซ่อน (PII Fields)
const SENSITIVE_FIELDS = [
'email', 'phone', 'name', 'surname',
'password', 'credit_card', 'national_id',
'address', 'ip_address', 'api_key', 'token',
'authorization', 'session_id', 'cookie'
];
// ฟังก์ชัน Hash สำหรับแทนที่ข้อมูลจริง
function hashValue(value, salt = 'HOLYSHEEP_SALT') {
if (typeof value !== 'string') {
return value;
}
return crypto
.createHash('sha256')
.update(salt + value)
.digest('hex')
.substring(0, 16);
}
// ทำ Anonymize ข้อมูลใน Object
function anonymizeObject(obj, depth = 0) {
if (depth > 10 || obj === null || typeof obj !== 'object') {
return obj;
}
const result = Array.isArray(obj) ? [] : {};
for (const key of Object.keys(obj)) {
const lowerKey = key.toLowerCase();
// ตรวจสอบว่าเป็นฟิลด์ที่ต้องซ่อนหรือไม่
const isSensitive = SENSITIVE_FIELDS.some(
field => lowerKey.includes(field)
);
if (isSensitive) {
result[key] = [REDACTED:${hashValue(String(obj[key]))}];
} else if (typeof obj[key] === 'object') {
result[key] = anonymizeObject(obj[key], depth + 1);
} else {
result[key] = obj[key];
}
}
return result;
}
// Mask IP Address
function maskIP(ip) {
if (!ip || typeof ip !== 'string') return '[REDACTED_IP]';
// IPv4: 192.168.1.100 -> 192.168.1.xxx
if (ip.includes('.')) {
const parts = ip.split('.');
return ${parts[0]}.${parts[1]}.${parts[2]}.xxx;
}
// IPv6: ตัดส่วนหลัง
if (ip.includes(':')) {
return ip.split(':').slice(0, 4).join(':') + ':xxxx';
}
return '[REDACTED_IP]';
}
// Middleware สำหรับ Express
function holySheepAnonymizeMiddleware(req, res, next) {
const startTime = Date.now();
// เก็บ Log แบบ Anonymized
const logEntry = {
timestamp: new Date().toISOString(),
request_id: req.headers['x-request-id'] || crypto.randomUUID(),
method: req.method,
path: req.path,
ip: maskIP(req.ip || req.connection.remoteAddress),
user_agent: req.headers['user-agent']?.substring(0, 100) || 'Unknown',
status_code: null,
latency_ms: null,
request_body: anonymizeObject(req.body || {}),
// ไม่เก็บ Headers ที่มีข้อมูลความลับ
headers: {
'content-type': req.headers['content-type'],
'x-api-version': req.headers['x-api-version']
}
};
// ตรวจจับ Response
const originalSend = res.send;
res.send = function(body) {
logEntry.status_code = res.statusCode;
logEntry.latency_ms = Date.now() - startTime;
// Anonymize Response ด้วย
try {
const parsedBody = JSON.parse(body);
logEntry.response_body = anonymizeObject(parsedBody);
} catch {
logEntry.response_body = '[BINARY_DATA]';
}
// ส่ง Log ไปเก็บที่ Storage
sendToSecureStorage(logEntry);
return originalSend.call(this, body);
};
next();
}
// ส่ง Log ไปเก็บแบบ Encrypted
async function sendToSecureStorage(logEntry) {
// เข้ารหัสก่อนส่ง
const encrypted = encryptLog(JSON.stringify(logEntry));
// ใช้ HolySheep API สำหรับ Log Storage
const response = await fetch('https://api.holysheep.ai/v1/logs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'X-Log-Schema-Version': '2024-01'
},
body: JSON.stringify({ encrypted_log: encrypted })
});
return response.json();
}
function encryptLog(data) {
const key = crypto.scryptSync(
process.env.LOG_ENCRYPTION_KEY,
'salt',
32
);
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return {
iv: iv.toString('hex'),
authTag: authTag.toString('hex'),
data: encrypted
};
}
module.exports = holySheepAnonymizeMiddleware;
ขั้นตอนที่ 2: ตั้งค่า API Gateway Configuration
# holy-sheep-gateway-config.yaml
gateway:
name: holy-sheep-production-gateway
version: "2.0"
region: southeast-asia
logging:
level: info
format: json
storage:
type: encrypted_s3
bucket: holy-sheep-logs-prod
prefix: anonymized-logs/
retention_days: 90
encryption: aes-256-gcm
anonymization:
enabled: true
rules:
- field_pattern: "*.email"
action: hash_sha256
preserve_format: true
- field_pattern: "*.phone"
action: mask_last_4
- field_pattern: "*.national_id"
action: redact
- field_pattern: "*.credit_card"
action: redact
- field_pattern: "*.password"
action: redact
- field_pattern: "headers.authorization"
action: redact
- field_pattern: "ip_address"
action: anonymize_subnet
compliance:
pdpa:
enabled: true
consent_required: true
data minimization: true
retention_policy: 90_days
gdpr:
enabled: true
right_to_erasure: true
data_portability: true
monitoring:
pii_detector:
enabled: true
alert_on_match: true
notification_webhook: "https://your-webhook.com/alert"
audit_trail:
enabled: true
immutable: true
blockchain_hash: optional
ขั้นตอนที่ 3: ใช้งานกับ Express.js
// server.js
const express = require('express');
const holySheepAnonymize = require('./anonymize-middleware');
const app = express();
// ใช้ Middleware สำหรับทำ Log ให้เป็นความลับ
app.use(holySheepAnonymizeMiddleware);
// ตัวอย่าง API Endpoint
app.post('/api/v1/submit-form', async (req, res) => {
// req.body จะถูก Anonymize ก่อนเก็บ Log อัตโนมัติ
// {
// email: "a1b2c3d4e5f6g7h8...", // Hash แล้ว
// phone: "xxx-xxx-9876", // Mask แล้ว
// name: "[REDACTED:hashed]", // Redact แล้ว
// message: "ข้อความที่ไม่มี PII" // เก็บปกติ
// }
const response = await processUserData(req.body);
res.json({
success: true,
message: 'ข้อมูลถูกบันทึกอย่างปลอดภัย',
request_id: req.headers['x-request-id']
});
});
// ตัวอย่าง API Endpoint สำหรับ AI Chat
app.post('/api/v1/chat', async (req, res) => {
const { model = 'gpt-4', messages } = req.body;
// เรียกใช้ HolySheep AI API
const response = await fetch('https://api.holysheep.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY}
},
body: JSON.stringify({
model: model,
messages: messages,
temperature: 0.7
})
});
const data = await response.json();
// Log Response ก็จะถูก Anonymize อัตโนมัติ
res.json(data);
});
app.listen(3000, () => {
console.log('🚀 HolySheep Gateway เริ่มทำงานพร้อม Log Anonymization');
});
รายละเอียดเทคนิคการทำ Log ให้เป็นความลับ
1. การ Hash แบบ One-Way สำหรับ Email และ Phone
สำหรับข้อมูลที่ต้องการเก็บเพื่อวิเคราะห์แต่ไม่ต้องการเห็นค่าจริง ใช้ Hash Function แบบ Salted SHA-256 ทำให้สามารถ Track ผู้ใช้เดิมได้โดยไม่รู้ค่าจริง
# log_anonymizer.py
import hashlib
import re
from typing import Any, Dict, List
from datetime import datetime
class LogAnonymizer:
def __init__(self, salt: str = "HOLYSHEEP_SALT_V1"):
self.salt = salt
self.sensitive_patterns = {
'email': re.compile(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'),
'phone_th': re.compile(r'(\+66|0)[0-9]{8,9}'),
'phone_intl': re.compile(r'\+?[0-9]{1,3}[-. ]?[0-9]{3}[-. ]?[0-9]{3}[-. ]?[0-9]{4}'),
'national_id': re.compile(r'[0-9]{13}'),
'credit_card': re.compile(r'[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}'),
'ip_v4': re.compile(r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b'),
'ip_v6': re.compile(r'[0-9a-fA-F:]+'),
}
def hash_value(self, value: str, prefix: str = "") -> str:
"""Hash ค่าด้วย Salt แบบ One-Way"""
if not isinstance(value, str):
value = str(value)
hash_obj = hashlib.sha256()
hash_obj.update(f"{self.salt}{value}".encode('utf-8'))
short_hash = hash_obj.hexdigest()[:16]
return f"[{prefix}{short_hash}]" if prefix else f"[HASH:{short_hash}]"
def mask_phone(self, phone: str) -> str:
"""Mask เบอร์โทรศัพท์ เก็บ 4 ตัวท้าย"""
digits = re.sub(r'[^\d]', '', phone)
if len(digits) >= 4:
return f"xxx-xxx-{digits[-4:]}"
return "[REDACTED_PHONE]"
def mask_ip(self, ip: str) -> str:
"""Mask IP เก็บ Subnet"""
if not ip:
return "[REDACTED_IP]"
# IPv4: 192.168.1.100 -> 192.168.1.xxx
if '.' in ip and not ':' in ip:
parts = ip.split('.')
if len(parts) == 4:
return f"{parts[0]}.{parts[1]}.{parts[2]}.xxx"
# IPv6: เก็บ 4 ส่วนแรก
if ':' in ip:
parts = ip.split(':')
return ':'.join(parts[:4]) + ':xxxx'
return "[REDACTED_IP]"
def anonymize_string(self, text: str) -> str:
"""Anonymize ข้อความทั้งหมดใน String"""
if not isinstance(text, str):
return text
result = text
# Mask Email
result = self.sensitive_patterns['email'].sub(
lambda m: self.hash_value(m.group(), 'EMAIL:'),
result
)
# Mask Phone Thailand
result = self.sensitive_patterns['phone_th'].sub(
lambda m: self.mask_phone(m.group()),
result
)
# Mask Phone International
result = self.sensitive_patterns['phone_intl'].sub(
lambda m: self.mask_phone(m.group()),
result
)
# Mask National ID (รหัสบัตรประชาชน)
result = self.sensitive_patterns['national_id'].sub(
lambda m: f"XXX-XX-{m.group()[-4:]}",
result
)
# Mask Credit Card
result = self.sensitive_patterns['credit_card'].sub(
lambda m: f"XXXX-XXXX-XXXX-{m.group()[-4:]}",
result
)
# Mask IP Address
result = self.sensitive_patterns['ip_v4'].sub(
lambda m: self.mask_ip(m.group()),
result
)
return result
def anonymize_dict(self, data: Dict[str, Any], path: str = "") -> Dict[str, Any]:
"""Anonymize Dictionary ทั้งหมด"""
if not isinstance(data, dict):
return data
result = {}
sensitive_keys = {
'email', 'phone', 'name', 'surname', 'first_name', 'last_name',
'password', 'passwd', 'secret', 'token', 'api_key', 'apikey',
'credit_card', 'card_number', 'national_id', 'id_card',
'address', 'street', 'ip', 'ip_address', 'client_ip',
'authorization', 'auth', 'bearer', 'session', 'session_id',
'cookie', 'user_id', 'userid', 'customer_id'
}
for key, value in data.items():
lower_key = key.lower().replace('-', '_').replace(' ', '_')
if lower_key in sensitive_keys:
# Hash ค่าที่สำคัญ
if lower_key in {'email', 'user_id', 'userid', 'customer_id'}:
result[key] = self.hash_value(str(value), f"{lower_key.upper()}:")
elif lower_key in {'phone'}:
result[key] = self.mask_phone(str(value))
elif lower_key in {'ip', 'ip_address', 'client_ip'}:
result[key] = self.mask_ip(str(value))
else:
result[key] = "[REDACTED]"
elif isinstance(value, dict):
result[key] = self.anonymize_dict(value, f"{path}/{key}")
elif isinstance(value, list):
result[key] = [self.anonymize_dict(item, f"{path}/{key}")
if isinstance(item, dict) else item
for item in value]
elif isinstance(value, str):
result[key] = self.anonymize_string(value)
else:
result[key] = value
return result
ทดสอบการทำงาน
if __name__ == "__main__":
anonymizer = LogAnonymizer()
test_data = {
"user_id": "user_12345",
"email": "[email protected]",
"phone": "081-234-5678",
"name": "สมหมาย",
"surname": "ไพรสินธ์",
"national_id": "1234567890123",
"ip_address": "192.168.1.100",
"order_details": {
"product": "AI Credits",
"price": 999.00
}
}
result = anonymizer.anonymize_dict(test_data)
print("ผลลัพธ์การ Anonymize:")
print(result)
การจัดเก็บ Log อย่างปลอดภัยตามมาตรฐาน PDPA
นอกจากการทำ Log ให้เป็นความลับแล้ว ยังต้องจัดเก็บอย่างถูกต้องตามกฎหมาย
หลักการจัดเก็บตาม PDPA
- Data Minimization — เก็บเฉพาะข้อมูลที่จำเป็นจริงๆ
- Purpose Limitation — ใช้ข้อมูลเฉพาะวัตถุประสงค์ที่แจ้งไว้
- Storage Limitation — กำหนดระยะเวลาการเก็บและลบเมื่อหมดความจำเป็น
- Integrity & Confidentiality — เข้ารหัสและป้องกันการเข้าถึงโดยไม่ได้รับอนุญาต
- Accountability — มี Audit Trail ที่ตรวจสอบได้
// secure-log-storage.js
const { S3Client, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3');
const { KMSClient, EncryptCommand, DecryptCommand } = require('@aws-sdk/client-kms');
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, PutCommand, QueryCommand } = require('@aws-sdk/lib-dynamodb');
const crypto = require('crypto');
class SecureLogStorage {
constructor(config) {
this.s3 = new S3Client({ region: config.region });
this.kms = new KMSClient({ region: config.region });
this.dynamo = DynamoDBDocumentClient.from(new DynamoDBClient({ region: config.region }));
this.bucketName = config.s3Bucket;
this.tableName = config.dynamoTable;
this.kmsKeyId = config.kmsKeyId;
this.retentionDays = config.retentionDays || 90;
}
// เข้ารหัส Log ก่อนจัดเก็บ
async encryptLog(logData) {
const plaintext = JSON.stringify(logData);
// Generate Data Key
const { plaintext: dataKey } = await this.kms.send(
new DecryptCommand({
CiphertextBlob: Buffer.from(process.env.DEK_BASE64, 'base64')
})
);
// Encrypt ด้วย AES-256-GCM
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', dataKey, iv);
let encrypted = cipher.update(plaintext, 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
const authTag = cipher.getAuthTag();
return {
iv: iv.toString('base64'),
authTag: authTag.toString('base64'),
encryptedData: encrypted.toString('base64'),
keyId: this.kmsKeyId
};
}
// จัดเก็บ Log แบบ Anonymized
async storeLog(logEntry) {
const timestamp = new Date();
const logId = crypto.randomUUID();
// เพิ่ม Metadata สำหรับ Compliance
const compliantLog = {
...logEntry,
_meta: {
log_id: logId,
stored_at: timestamp.toISOString(),
retention_until: new Date(
timestamp.getTime() + this.retentionDays * 24 * 60 * 60 * 1000
).toISOString(),
encryption_version: 'AES-256-GCM-v1',
anonymized: true,
pii_detected: logEntry._meta?.pii_found || false
}
};
// เข้ารหัสก่อนจัดเก็บ
const encrypted = await this.encryptLog(compliantLog);
// คำนวณ Checksum
const checksum = crypto
.createHash('sha256')
.update(JSON.stringify(logEntry))
.digest('hex');
// จัดเก็บลง S3
const s3Key = logs/${timestamp.getFullYear()}/${String(timestamp.getMonth() + 1).padStart(2, '0')}/${String(timestamp.getDate()).padStart(2, '0')}/${logId}.enc;
await this.s3.send(new PutObjectCommand({
Bucket: this.bucketName,
Key: s3Key,
Body: JSON.stringify(encrypted),
ServerSideEncryption: 'aws:kms',
SSEKMSKeyId: this.kmsKeyId,
Metadata: {
'checksum': checksum,
'retention-days': String(this.retentionDays),
'stored-at': timestamp.toISOString()
}
}));
// จัดเก็บ Index ลง DynamoDB สำหรับ Query
await this.dynamo.send(new PutCommand({
TableName: this.tableName,
Item: {
log_id: logId,
timestamp: timestamp.toISOString(),
retention_until: compliantLog._meta.retention_until,
request_id: logEntry.request_id,
method: logEntry.method,
path: logEntry.path,
status_code: logEntry.status_code,
latency_ms: logEntry.latency_ms,
checksum: checksum,
s3_key: s3Key,
// เก็บเฉพาะ Metadata ที่ไม่มี PII
metadata: {
region: logEntry.region || 'unknown',
service: logEntry.service || 'api-gateway',
version: logEntry.version || 'v1'
}
}
}));
return { logId, s3Key, checksum };
}
// Query Log แบบ Decrypt
async getLog(logId) {
const result = await this.dynamo.send(new QueryCommand({
TableName: this.tableName,
KeyConditionExpression: 'log_id = :id',
ExpressionAttributeValues: {
':id': logId
}
}));
if (!result.Items || result.Items.length === 0) {
return null;
}
const index = result.Items[0];
// ดึง Log จาก S3
const s3Response = await this.s3.send(new GetObjectCommand({
Bucket: this.bucketName,
Key: index.s3_key
}));
const encryptedData = JSON.parse(
await s3Response.Body.transformToString()
);
// Decrypt Log
const decrypted = await this.decryptLog(encryptedData);