OpenAI SDK Security: Best Practices for Production Applications
Seguridad en Aplicaciones OpenAI
La seguridad es crucial al implementar aplicaciones con OpenAI SDK en producción. Aquí te mostramos las mejores prácticas para proteger tu aplicación y los datos de tus usuarios.
1. Gestión Segura de API Keys
Almacenamiento Seguro
// ❌ NUNCA hagas esto
const openai = new OpenAI({
apiKey: "sk-1234567890abcdef" // Exposición directa
});
// ✅ Forma correcta
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
// ✅ Con validación
function validateAPIKey() {
const apiKey = process.env.OPENAI_API_KEY;
if (!apiKey || !apiKey.startsWith('sk-')) {
throw new Error('Invalid OpenAI API key');
}
return apiKey;
}Rotación de API Keys
class APIKeyManager {
constructor() {
this.keys = [];
this.currentKeyIndex = 0;
}
addKey(key) {
this.keys.push(key);
}
getCurrentKey() {
return this.keys[this.currentKeyIndex];
}
rotateKey() {
this.currentKeyIndex = (this.currentKeyIndex + 1) % this.keys.length;
}
async validateKey(key) {
try {
const testOpenAI = new OpenAI({ apiKey: key });
await testOpenAI.models.list();
return true;
} catch (error) {
return false;
}
}
}2. Rate Limiting y Throttling
Implementación de Rate Limiting
class RateLimiter {
constructor(maxRequests, timeWindow) {
this.maxRequests = maxRequests;
this.timeWindow = timeWindow;
this.requests = new Map();
}
async checkLimit(userId) {
const now = Date.now();
const userRequests = this.requests.get(userId) || [];
// Filtrar requests dentro del timeWindow
const recentRequests = userRequests.filter(
timestamp => now - timestamp < this.timeWindow
);
if (recentRequests.length >= this.maxRequests) {
throw new Error('Rate limit exceeded');
}
recentRequests.push(now);
this.requests.set(userId, recentRequests);
return true;
}
}3. Validación y Sanitización de Inputs
Sanitización de Prompts
class InputSanitizer {
static sanitizePrompt(prompt) {
// Remover caracteres peligrosos
let sanitized = prompt
.replace(/[<>]/g, '') // Remover HTML tags
.replace(/javascript:/gi, '') // Remover JavaScript
.replace(/onw+=/gi, '') // Remover event handlers
.trim();
// Limitar longitud
if (sanitized.length > 4000) {
sanitized = sanitized.substring(0, 4000);
}
return sanitized;
}
static validateInput(input) {
const rules = {
maxLength: 4000,
allowedChars: /^[a-zA-Z0-9s.,!?-_()]+$/,
blockedWords: ['password', 'secret', 'key', 'token']
};
if (input.length > rules.maxLength) {
throw new Error('Input too long');
}
if (!rules.allowedChars.test(input)) {
throw new Error('Invalid characters detected');
}
if (rules.blockedWords.some(word =>
input.toLowerCase().includes(word))) {
throw new Error('Sensitive information detected');
}
return true;
}
}4. Logging y Auditoría
Sistema de Logging Seguro
class SecureLogger {
constructor() {
this.logs = [];
}
logRequest(userId, prompt, response, metadata) {
const logEntry = {
timestamp: new Date().toISOString(),
userId: this.hashUserId(userId),
promptHash: this.hashContent(prompt),
responseLength: response.length,
metadata: {
model: metadata.model,
tokens: metadata.tokens,
cost: metadata.cost
}
};
this.logs.push(logEntry);
// No almacenar contenido sensible
console.log('API Request logged:', logEntry);
}
hashUserId(userId) {
return require('crypto')
.createHash('sha256')
.update(userId)
.digest('hex')
.substring(0, 16);
}
hashContent(content) {
return require('crypto')
.createHash('sha256')
.update(content)
.digest('hex')
.substring(0, 16);
}
}5. Manejo de Errores Seguro
Error Handling Robusto
class SecureErrorHandler {
static async handleOpenAIError(error, context) {
const errorInfo = {
timestamp: new Date().toISOString(),
context: context,
errorType: error.constructor.name
};
// No exponer detalles internos
if (error.code === 'insufficient_quota') {
return {
message: 'Service temporarily unavailable',
code: 'SERVICE_UNAVAILABLE'
};
}
if (error.code === 'invalid_api_key') {
return {
message: 'Authentication failed',
code: 'AUTH_ERROR'
};
}
// Log error interno
console.error('OpenAI Error:', errorInfo);
return {
message: 'An error occurred processing your request',
code: 'INTERNAL_ERROR'
};
}
}6. Encriptación de Datos
Encriptación de Conversaciones
const crypto = require('crypto');
class DataEncryption {
constructor(secretKey) {
this.secretKey = secretKey;
this.algorithm = 'aes-256-gcm';
}
encrypt(text) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipher(this.algorithm, this.secretKey);
cipher.setAAD(Buffer.from('openai-sdk', 'utf8'));
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return {
encrypted,
iv: iv.toString('hex'),
authTag: authTag.toString('hex')
};
}
decrypt(encryptedData) {
const decipher = crypto.createDecipher(
this.algorithm,
this.secretKey
);
decipher.setAAD(Buffer.from('openai-sdk', 'utf8'));
decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));
let decrypted = decipher.update(
encryptedData.encrypted,
'hex',
'utf8'
);
decrypted += decipher.final('utf8');
return decrypted;
}
}7. Monitoreo de Seguridad
Detección de Anomalías
class SecurityMonitor {
constructor() {
this.suspiciousPatterns = [
/password|secret|key|token/gi,
/ 0.7) {
await this.flagSuspiciousActivity(userId, prompt, suspiciousScore);
}
// Monitorear actividad del usuario
this.trackUserActivity(userId);
// Verificar rate limiting
if (this.isUserExceedingLimits(userId)) {
throw new Error('User activity limit exceeded');
}
}
calculateSuspiciousScore(prompt) {
let score = 0;
this.suspiciousPatterns.forEach(pattern => {
if (pattern.test(prompt)) {
score += 0.3;
}
});
// Detectar prompts muy largos o repetitivos
if (prompt.length > 2000) score += 0.2;
if (this.isRepetitive(prompt)) score += 0.3;
return Math.min(score, 1.0);
}
async flagSuspiciousActivity(userId, prompt, score) {
console.warn(`Suspicious activity detected for user ${userId}: ${score}`);
// Implementar alertas y bloqueos según sea necesario
}
}8. Configuración de Producción
Variables de Entorno Seguras
// .env.production
OPENAI_API_KEY=sk-your-secure-key-here
OPENAI_ORG_ID=org-your-org-id
ENCRYPTION_KEY=your-encryption-key-32-chars
RATE_LIMIT_MAX=100
RATE_LIMIT_WINDOW=3600000
LOG_LEVEL=info
ENABLE_MONITORING=trueConfiguración de Seguridad
const securityConfig = {
apiKey: {
rotationInterval: 24 * 60 * 60 * 1000, // 24 horas
maxUsage: 10000, // tokens por día
alertThreshold: 8000
},
rateLimiting: {
maxRequests: 100,
timeWindow: 3600000, // 1 hora
burstLimit: 10
},
monitoring: {
enableAnomalyDetection: true,
logLevel: 'info',
alertChannels: ['email', 'slack']
},
encryption: {
algorithm: 'aes-256-gcm',
keyRotation: 7 * 24 * 60 * 60 * 1000 // 7 días
}
};Checklist de Seguridad
- ✅ API keys almacenadas de forma segura
- ✅ Rate limiting implementado
- ✅ Validación de inputs
- ✅ Logging sin datos sensibles
- ✅ Manejo seguro de errores
- ✅ Encriptación de datos
- ✅ Monitoreo de seguridad
- ✅ Configuración de producción