12 KiB
Security Configuration
🌍 Language / 语言
Comprehensive security guide for deploying AI Proxy Worker in production environments. Follow these best practices to ensure your deployment is secure and protected against common threats.
🔐 Authentication & Authorization
API Key Security
Protect your AI service API keys:
// ✅ Good: Store in Cloudflare secrets
wrangler secret put DEEPSEEK_API_KEY
// ❌ Bad: Never hardcode in source code
const API_KEY = "sk-1234567890abcdef"; // NEVER DO THIS
Proxy Key Configuration
Set up secure proxy access:
// Strong proxy key generation
const PROXY_KEY = crypto.randomUUID() + crypto.randomUUID();
// Set as Cloudflare secret
wrangler secret put PROXY_KEY
Multi-tier Authentication
Implement layered security:
async function authenticateRequest(request, env) {
const authHeader = request.headers.get('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return { valid: false, error: 'Missing or invalid authorization header' };
}
const token = authHeader.substring(7);
// Verify proxy key
if (token !== env.PROXY_KEY) {
return { valid: false, error: 'Invalid proxy key' };
}
return { valid: true };
}
🛡️ Input Validation & Sanitization
Request Validation
Validate all incoming requests:
function validateChatRequest(data) {
const errors = [];
// Required fields
if (!data.messages || !Array.isArray(data.messages)) {
errors.push('messages must be an array');
}
if (!data.model || typeof data.model !== 'string') {
errors.push('model must be a string');
}
// Message validation
data.messages?.forEach((msg, index) => {
if (!msg.role || !['user', 'assistant', 'system'].includes(msg.role)) {
errors.push(`messages[${index}].role must be user, assistant, or system`);
}
if (!msg.content || typeof msg.content !== 'string') {
errors.push(`messages[${index}].content must be a non-empty string`);
}
// Content length limits
if (msg.content.length > 100000) {
errors.push(`messages[${index}].content exceeds maximum length`);
}
});
// Parameter validation
if (data.temperature !== undefined) {
if (typeof data.temperature !== 'number' || data.temperature < 0 || data.temperature > 2) {
errors.push('temperature must be a number between 0 and 2');
}
}
return {
valid: errors.length === 0,
errors
};
}
Content Filtering
Implement content safety checks:
function containsUnsafeContent(text) {
const unsafePatterns = [
/\b(password|secret|key|token)\s*[:=]\s*\w+/i,
/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/, // Credit card patterns
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/ // Email patterns (if needed)
];
return unsafePatterns.some(pattern => pattern.test(text));
}
// Usage in request handler
if (containsUnsafeContent(message.content)) {
return new Response(JSON.stringify({
error: 'content_rejected',
message: 'Request contains potentially unsafe content'
}), { status: 400 });
}
🚫 Rate Limiting & DDoS Protection
Request Rate Limiting
Implement rate limiting to prevent abuse:
class RateLimiter {
constructor(maxRequests = 100, windowMs = 60000) {
this.maxRequests = maxRequests;
this.windowMs = windowMs;
this.requests = new Map();
}
isAllowed(clientId) {
const now = Date.now();
const windowStart = now - this.windowMs;
// Clean old entries
for (const [id, timestamps] of this.requests.entries()) {
const validTimestamps = timestamps.filter(ts => ts > windowStart);
if (validTimestamps.length === 0) {
this.requests.delete(id);
} else {
this.requests.set(id, validTimestamps);
}
}
// Check current client
const clientRequests = this.requests.get(clientId) || [];
const recentRequests = clientRequests.filter(ts => ts > windowStart);
if (recentRequests.length >= this.maxRequests) {
return false;
}
// Add current request
recentRequests.push(now);
this.requests.set(clientId, recentRequests);
return true;
}
getRemainingRequests(clientId) {
const clientRequests = this.requests.get(clientId) || [];
const windowStart = Date.now() - this.windowMs;
const recentRequests = clientRequests.filter(ts => ts > windowStart);
return Math.max(0, this.maxRequests - recentRequests.length);
}
}
// Usage
const rateLimiter = new RateLimiter(100, 60000); // 100 requests per minute
async function handleRequest(request, env) {
const clientId = getClientId(request);
if (!rateLimiter.isAllowed(clientId)) {
return new Response(JSON.stringify({
error: 'rate_limit_exceeded',
message: 'Too many requests. Please try again later.',
retryAfter: 60
}), {
status: 429,
headers: {
'Content-Type': 'application/json',
'Retry-After': '60'
}
});
}
// Process request...
}
IP-based Protection
Implement IP-based security measures:
function getClientIP(request) {
return request.headers.get('CF-Connecting-IP') ||
request.headers.get('X-Forwarded-For') ||
'unknown';
}
const BLOCKED_IPS = new Set([
'192.168.1.100',
'10.0.0.50'
]);
function isBlockedIP(ip) {
return BLOCKED_IPS.has(ip);
}
// Usage in request handler
const clientIP = getClientIP(request);
if (isBlockedIP(clientIP)) {
return new Response('Access denied', { status: 403 });
}
🔒 Data Protection
Sensitive Data Handling
Never log sensitive information:
function sanitizeForLogging(data) {
const sanitized = { ...data };
// Remove sensitive fields
delete sanitized.api_key;
delete sanitized.authorization;
delete sanitized.password;
// Truncate long content
if (sanitized.messages) {
sanitized.messages = sanitized.messages.map(msg => ({
...msg,
content: msg.content.length > 100 ?
msg.content.substring(0, 100) + '...[truncated]' :
msg.content
}));
}
return sanitized;
}
// Usage
console.log('Request received:', sanitizeForLogging(requestData));
Response Sanitization
Clean responses before sending:
function sanitizeResponse(response) {
// Remove internal fields
const sanitized = { ...response };
delete sanitized.internal_id;
delete sanitized.debug_info;
delete sanitized.upstream_headers;
return sanitized;
}
🌐 CORS Configuration
Secure CORS Setup
Configure CORS properly:
function setCORSHeaders(response, origin) {
const allowedOrigins = [
'https://yourdomain.com',
'https://app.yourdomain.com',
'https://localhost:3000' // Development only
];
if (allowedOrigins.includes(origin)) {
response.headers.set('Access-Control-Allow-Origin', origin);
}
response.headers.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
response.headers.set('Access-Control-Max-Age', '86400');
return response;
}
// Handle preflight requests
if (request.method === 'OPTIONS') {
const origin = request.headers.get('Origin');
const response = new Response(null, { status: 204 });
return setCORSHeaders(response, origin);
}
📝 Security Headers
Essential Security Headers
Add security headers to all responses:
function addSecurityHeaders(response) {
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-XSS-Protection', '1; mode=block');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set('Content-Security-Policy', "default-src 'none'");
// Remove server information
response.headers.delete('Server');
return response;
}
🔍 Security Monitoring
Security Event Logging
Log security-related events:
function logSecurityEvent(event, details) {
console.warn('Security event:', {
event,
timestamp: new Date().toISOString(),
...details
});
// Send to security monitoring service
if (env.SECURITY_WEBHOOK) {
fetch(env.SECURITY_WEBHOOK, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
service: 'ai-proxy-worker',
event,
timestamp: new Date().toISOString(),
...details
})
}).catch(err => console.error('Failed to send security alert:', err));
}
}
// Usage
logSecurityEvent('authentication_failure', {
ip: getClientIP(request),
userAgent: request.headers.get('User-Agent'),
path: new URL(request.url).pathname
});
Anomaly Detection
Detect unusual patterns:
class AnomalyDetector {
constructor() {
this.requestPatterns = new Map();
}
recordRequest(clientId, endpoint) {
const key = `${clientId}:${endpoint}`;
const now = Date.now();
if (!this.requestPatterns.has(key)) {
this.requestPatterns.set(key, []);
}
const requests = this.requestPatterns.get(key);
requests.push(now);
// Keep only recent requests (last hour)
const oneHourAgo = now - 3600000;
const recentRequests = requests.filter(ts => ts > oneHourAgo);
this.requestPatterns.set(key, recentRequests);
// Detect anomalies
if (recentRequests.length > 1000) { // More than 1000 requests per hour
logSecurityEvent('high_frequency_requests', {
clientId,
endpoint,
requestCount: recentRequests.length
});
}
}
}
🛠️ Environment Configuration
Production Environment Variables
Secure environment setup:
# Required secrets
wrangler secret put DEEPSEEK_API_KEY
wrangler secret put PROXY_KEY
# Optional security settings
wrangler secret put SECURITY_WEBHOOK
wrangler secret put RATE_LIMIT_MAX_REQUESTS
wrangler secret put ALLOWED_ORIGINS
Configuration Validation
Validate environment on startup:
function validateEnvironment(env) {
const required = ['DEEPSEEK_API_KEY'];
const missing = required.filter(key => !env[key]);
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
}
// Validate API key format
if (!env.DEEPSEEK_API_KEY.startsWith('sk-')) {
console.warn('DEEPSEEK_API_KEY may be invalid - should start with sk-');
}
return true;
}
🚨 Incident Response
Security Incident Checklist
When a security incident is detected:
-
Immediate Response
- Identify the scope of the incident
- Block malicious traffic if possible
- Preserve logs and evidence
-
Assessment
- Determine what data may have been accessed
- Assess the impact on users and services
- Check for ongoing threats
-
Containment
- Rotate compromised API keys
- Update security rules
- Deploy patches if needed
-
Recovery
- Restore normal operations
- Monitor for continued threats
- Validate security measures
-
Post-Incident
- Document lessons learned
- Update security procedures
- Notify stakeholders if required
Emergency Procedures
// Emergency shutdown capability
async function emergencyShutdown(reason) {
logSecurityEvent('emergency_shutdown', { reason });
// Return maintenance mode response
return new Response(JSON.stringify({
error: 'service_unavailable',
message: 'Service temporarily unavailable for maintenance',
timestamp: new Date().toISOString()
}), {
status: 503,
headers: {
'Content-Type': 'application/json',
'Retry-After': '3600'
}
});
}
📋 Security Checklist
Pre-deployment Security Review
- All API keys stored as secrets
- Input validation implemented
- Rate limiting configured
- CORS properly configured
- Security headers added
- Logging sanitized
- Error messages don't leak information
- Dependencies updated
- Security monitoring enabled
Regular Security Maintenance
- Review access logs monthly
- Update dependencies quarterly
- Rotate API keys annually
- Test incident response procedures
- Review and update security policies
Security is an ongoing process 🔒
Regular reviews and updates ensure your AI Proxy Worker remains secure against evolving threats.