บทนำ
การนำ AI API มาใช้งานในแอปพลิเคชัน Frontend นั้นสะดวกและรวดเร็ว แต่หากไม่ระวัง คุณอาจสร้างช่องโหว่ด้านความปลอดภัยที่ทำให้ API Key รั่วไหลไปยังผู้ไม่หวังดีได้โดยไม่รู้ตัว บทความนี้จะอธิบาย 3 รูปแบบสถาปัตยกรรมที่ช่วยป้องกันปัญหานี้ พร้อมตัวอย่างโค้ดที่นำไปใช้งานได้จริง
---
ทำไม API Key ถึงต้องป้องกัน?
เมื่อคุณฝัง API Key ไว้ในโค้ด Frontend ไม่ว่าจะเป็น React, Vue, หรือ Angular ผู้ใช้งานทุกคนสามารถเปิด Developer Tools ดู Network Tab หรือ Source Code แล้วค้นหา Key ของคุณได้ทันที นี่คือปัญหาพื้นฐานที่ต้องแก้ไขตั้งแต่ต้น
---
รูปแบบที่ 1: Backend Proxy (สถาปัตยกรรมแนะนำ)
แนวคิด
แทนที่จะเรียก API ตรงจาก Frontend ให้ Frontend ส่งคำขอไปยัง Backend Server ของตัวเองก่อน แล้วให้ Backend ไปเรียก AI API อีกที วิธีนี้ทำให้ API Key อยู่เฉพาะใน Backend เท่านั้น
ข้อดี
- API Key ไม่มีทางถูกเปิดเผยใน Client Side เลย
- สามารถทำ Caching, Rate Limiting และ Logging ได้ง่าย
- ปรับแต่ง Request/Response ได้ตามต้องการ
ข้อเสีย
- เพิ่ม Latency จากการผ่าน Middleware
- ต้องดูแล Server เพิ่มขึ้น
ตัวอย่างโค้ด Backend (Node.js/Express)
// server.js
import express from 'express';
import axios from 'axios';
const app = express();
app.use(express.json());
// API Key อยู่เฉพาะใน Backend เท่านั้น
const HOLYSHEEP_API_KEY = process.env.HOLYSHEEP_API_KEY;
const HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1';
app.post('/api/chat', async (req, res) => {
try {
const { messages } = req.body;
const response = await axios.post(
${HOLYSHEEP_BASE_URL}/chat/completions,
{
model: 'gpt-4.1',
messages: messages
},
{
headers: {
'Authorization': Bearer ${HOLYSHEEP_API_KEY},
'Content-Type': 'application/json'
},
timeout: 30000
}
);
res.json(response.data);
} catch (error) {
console.error('Proxy error:', error.message);
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(3000, () => {
console.log('Proxy server running on port 3000');
});
ตัวอย่างโค้ด Frontend (React)
// apiService.js
const API_BASE = '/api';
export async function sendChat(messages) {
const response = await fetch(${API_BASE}/chat, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ messages })
});
if (!response.ok) {
throw new Error('Request failed');
}
return response.json();
}
---
รูปแบบที่ 2: Cloudflare Workers / Edge Functions
แนวคิด
ใช้ Edge Functions เป็นตัวกลางระหว่าง Frontend และ AI API ซึ่งทำงานบน Edge Network ใกล้ผู้ใช้งาน ทำให้ได้ Latency ต่ำกว่า Traditional Backend และ API Key ยังคงถูกซ่อนไว้
ข้อดี
- Latency ต่ำกว่า Traditional Backend
- Scale ได้อัตโนมัติโดยไม่ต้องจัดการ Server
- มี DDoS Protection และ SSL Termination ในตัว
ข้อเสีย
- ต้องเรียนรู้ Platform ใหม่ (Cloudflare Workers, Vercel Edge Functions)
- มีข้อจำกัดด้าน Cold Start และ Execution Time
ตัวอย่างโค้ด Cloudflare Workers
// worker.js
export default {
async fetch(request, env) {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
try {
const { messages } = await request.json();
const response = await fetch('https://api.holysheep.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': Bearer ${env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-4.1',
messages: messages
})
});
const data = await response.json();
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
};
wrangler.toml
name = "ai-proxy-worker"
main = "worker.js"
compatibility_date = "2024-01-01"
[vars]
ALLOWED_ORIGIN = "https://yourdomain.com"
---
รูปแบบที่ 3: API Gateway with Key Rotation
แนวคิด
ใช้ API Gateway เป็นตัวจัดการ API Key ทั้งหมด รวมถึงการหมุนคีย์อัตโนมัติ (Automatic Key Rotation) เพื่อลดความเสี่ยงหากคีย์ใดรั่วไหล
ข้อดี
- จัดการ Key ได้จากที่เดียวทั้งหมด
- หมุนคีย์ได้อัตโนมัติโดยไม่ต้อง Deploy ใหม่
- มีระบบ Logging และ Monitoring ในตัว
ข้อเสีย
- ต้องใช้บริการ Third-party (มีค่าใช้จ่ายเพิ่มเติม)
- ซับซ้อนในการตั้งค่าเริ่มต้น
ตัวอย่างการตั้งค่า AWS API Gateway
# api-gateway-config.yaml
Resources:
ProxyResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RESTAPI
ParentId: !GetAtt RESTAPI.RootResourceId
PathPart: 'ai-proxy'
ProxyMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RESTAPI
ResourceId: !Ref ProxyResource
HttpMethod: POST
AuthorizationType: NONE
Integration:
Type: HTTP_PROXY
Uri: https://api.holysheep.ai/v1/chat/completions
IntegrationHttpMethod: POST
APIKey:
Type: AWS::ApiGateway::ApiKey
Properties:
Name: holy-sheep-api-key
Enabled: true
UsagePlan:
Type: AWS::ApiGateway::UsagePlan
Properties:
ApiStages:
- ApiId: !Ref RESTAPI
Stage: prod
Quota:
Limit: 1000000
Period: MONTH
การหมุนคีย์อัตโนมัติ (Python Script)
# rotate_key.py
import os
import requests
import boto3
from datetime import datetime, timedelta
def rotate_api_key():
"""
หมุนคีย์เก่าและสร้างคีย์ใหม่
ควรตั้งเป็น Cron Job รันทุก 30 วัน
"""
# ดึงคีย์เก่าจาก Secrets Manager
secret_name = "holy-sheep-api-key"
region_name = "us-east-1"
session = boto3.session.Session()
client = session.client('secretsmanager', region_name=region_name)
try:
old_key = client.get_secret_value(SecretId=secret_name)
old_key_value = old_key['SecretString']
# สร้างคีย์ใหม่ (จาก HolySheep Dashboard)
new_key = create_holysheep_key()
# อัพเดท Secrets Manager
client.put_secret_value(
SecretId=secret_name,
SecretString=new_key
)
# ยกเลิกคีย์เก่า
revoke_holysheep_key(old_key_value)
print(f"Key rotated successfully at {datetime.now()}")
except Exception as e:
print(f"Rotation failed: {e}")
raise
def create_holysheep_key():
"""สร้าง API Key ใหม่จาก HolySheep"""
response = requests.post(
'https://api.holysheep.ai/v1/keys',
headers={'Authorization': 'Bearer YOUR_ADMIN_KEY'}
)
return response.json()['api_key']
def revoke_holysheep_key(key):
"""ยกเลิก API Key เก่า"""
requests.delete(
f'https://api.holysheep.ai/v1/keys/{key}',
headers={'Authorization': 'Bearer YOUR_ADMIN_KEY'}
)
if __name__ == '__main__':
rotate_api_key()
---
เปรียบเทียบ 3 รูปแบบ
| รูปแบบ | ความปลอดภัย | Latency | ความซับซ้อน | ค่าใช้จ่าย |
|--------|-------------|---------|-------------|-----------|
| Backend Proxy | สูงมาก | ปานกลาง | ปานกลาง | Server Hosting |
| Edge Functions | สูง | ต่ำ | ต่ำ | Pay-per-use |
| API Gateway | สูงมาก | ปานกลาง | สูง | Monthly Fee |
---
คำแนะนำในการเลือกรูปแบบ
**เลือก Backend Proxy** หากคุณมี Backend Team และต้องการควบคุม Logic ทั้งหมดเอง
**เลือก Edge Functions** หากคุณต้องการ Latency ต่ำและไม่อยากดูแล Server
**เลือก API Gateway** หากคุณต้องการ Enterprise-grade Security และมีงบประมาณรองรับ
---
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
ข้อผิดพลาดที่ 1: ฝัง API Key ไว้ใน JavaScript Bundle
**อาการ:** เปิด DevTools หรือ Source Map แล้วเจอ API Key อยู่ในโค้ด
**สาเหตุ:** การใช้ Environment Variable ผิดวิธี เช่น
const API_KEY = "sk-xxx" ตรงๆ
**วิธีแก้ไข:**
// ผิด ❌
const API_KEY = "sk-abc123def456";
// ถูกต้อง ✅ - ส่งผ่าน Proxy แทน
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages })
});
**หลักการ:** Frontend ต้องส่ง Request ไปที่ Backend ของตัวเองเท่านั้น อย่าเรียก AI API ตรง
---
ข้อผิดพลาดที่ 2: ใส่ API Key ใน .env File แล้ว Commit lên GitHub
**อาการ:** ตรวจสอบ GitHub History แล้วเจอ API Key ที่ถูก Commit ไปแล้ว
**สาเหตุ:** ไม่ได้เพิ่ม .env ใน .gitignore หรือ Commit โดยไม่ตรวจสอบ
**วิธีแก้ไข:**
# สร้างไฟล์ .gitignore
echo "HOLYSHEEP_API_KEY=.env" >> .gitignore
echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
echo ".env.production" >> .gitignore
ลบ API Key ที่ Commit ไปแล้วออกจาก History
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch .env" \
--prune-empty --tag-name-filter cat -- --all
หมุนคีย์ทันทีหลังจาก Commit โดน Leak
เพราะ GitHub History อาจถูก Clone ไปแล้ว
**หลักการ:** ป้องกันที่ต้นทางด้วย .gitignore และเตรียมแผนหมุนคีย์ฉุกเฉินเสมอ
---
ข้อผิดพลาดที่ 3: ไม่ตรวจสอบ Origin/Referer Header
**อาการ:** API Key ถูกใช้งานจาก Domain อื่นที่ไม่ใช่ของเรา
**สาเหตุ:** Proxy Server ไม่ได้ตรวจสอบว่า Request มาจากที่ไหน
**วิธีแก้ไข:**
```javascript
// server.js - เพิ่มการตรวจสอบ Origin
const ALLOWED_ORIGINS = [
'https://yourdomain.com',
'https://www.yourdomain.com',
'http://localhost:3000' // สำหรับ Development
];
app.use((req, res, next) => {
const origin = req.headers.origin;
if (ALLOWED_ORIGINS.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
// ตรว
แหล่งข้อมูลที่เกี่ยวข้อง
บทความที่เกี่ยวข้อง