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=true

Configuració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

Subscribe to AI.TDD Articles

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe