ML Engine Updates: - Updated BTCUSD with Polygon API data (2024-2025): 215,699 new records - Re-trained all ML models: Attention (R²: 0.223), Base, Metamodel (87.3% confidence) - Backtest results: +176.71R profit with aggressive_filter strategy Documentation Consolidation: - Created docs/99-analisis/_MAP.md index with 13 new analysis documents - Consolidated inventories: removed duplicates from orchestration/inventarios/ - Updated ML_INVENTORY.yml with BTCUSD metrics and training results - Added execution reports: FASE11-BTCUSD, correction issues, alignment validation Architecture & Integration: - Updated all module documentation with NEXUS v3.4 frontmatter - Fixed _MAP.md indexes across all folders - Updated orchestration plans and traces Files: 229 changed, 5064 insertions(+), 1872 deletions(-) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
14 KiB
14 KiB
| id | title | type | status | priority | epic | project | version | created_date | updated_date |
|---|---|---|---|---|---|---|---|---|---|
| ET-PAY-006 | Seguridad PCI DSS | Technical Specification | Done | Alta | OQI-005 | trading-platform | 1.0.0 | 2025-12-05 | 2026-01-04 |
ET-PAY-006: Seguridad PCI DSS
Epic: OQI-005 Pagos y Stripe Versión: 1.0 Fecha: 2025-12-05
1. Descripción
Implementación de medidas de seguridad para cumplimiento PCI DSS:
- Tokenización de tarjetas con Stripe
- No almacenamiento de datos sensibles
- Validaciones de seguridad
- Logs de auditoría
- Encriptación de datos
- Prevención de fraude
2. Arquitectura de Seguridad
┌─────────────────────────────────────────────────────────────────┐
│ Payment Security Stack │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Frontend Backend Stripe │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Stripe.js │───►│ Tokenization │───►│ Vault │ │
│ │ (PCI SAQ-A) │ │ Only Tokens │ │ (Card Data) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ │
│ │ Fraud │ │
│ │ Detection │ │
│ └──────────────┘ │
│ │
│ ┌──────────────┐ │
│ │ Audit Logs │ │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
3. PCI DSS Requirements
3.1 Nivel de Cumplimiento
SAQ-A (Self-Assessment Questionnaire A)
Trading Platform califica para SAQ-A porque:
- No almacena, procesa ni transmite datos de tarjetas
- Usa Stripe.js y Elements (redirección completa a Stripe)
- Solo maneja tokens de Stripe, no datos de tarjetas
3.2 Requisitos SAQ-A
- Usar solo proveedores PCI DSS compliant (Stripe)
- No almacenar datos sensibles (CVV, track data, PIN)
- Mantener política de seguridad
- Usar conexiones seguras (HTTPS)
- No usar contraseñas por defecto
4. Implementación de Seguridad
4.1 Tokenization Service
// src/services/security/tokenization.service.ts
import { StripeService } from '../stripe/stripe.service';
import { AppError } from '../../utils/errors';
export class TokenizationService {
private stripeService: StripeService;
constructor() {
this.stripeService = new StripeService();
}
/**
* NUNCA acepta datos de tarjeta directamente
* Solo acepta tokens de Stripe
*/
async validatePaymentToken(token: string): Promise<boolean> {
// Verificar que es un token válido de Stripe
if (!token.startsWith('pm_') && !token.startsWith('tok_')) {
throw new AppError('Invalid payment token format', 400);
}
return true;
}
/**
* Guardar payment method (solo token)
*/
async savePaymentMethod(params: {
user_id: string;
payment_method_id: string; // Token de Stripe, NO datos de tarjeta
customer_id: string;
}): Promise<void> {
// Validar token
await this.validatePaymentToken(params.payment_method_id);
// Adjuntar a customer en Stripe
await this.stripeService.attachPaymentMethod(
params.payment_method_id,
params.customer_id
);
// Guardar solo metadata en DB (NO datos de tarjeta)
// Ver ET-PAY-001 para estructura de tabla payment_methods
}
}
4.2 Data Validation
// src/middlewares/payment-validation.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { AppError } from '../utils/errors';
/**
* Valida que NO se envíen datos sensibles de tarjetas
*/
export const preventCardDataSubmission = (
req: Request,
res: Response,
next: NextFunction
): void => {
const sensitiveFields = [
'card_number',
'cvv',
'cvc',
'card_cvv',
'expiry',
'exp_month',
'exp_year',
];
const body = JSON.stringify(req.body).toLowerCase();
for (const field of sensitiveFields) {
if (body.includes(field)) {
throw new AppError(
'Card data not accepted. Use Stripe tokenization.',
400
);
}
}
next();
};
4.3 Fraud Detection
// src/services/security/fraud-detection.service.ts
import { PaymentRepository } from '../../modules/payments/payment.repository';
import { logger } from '../../utils/logger';
export class FraudDetectionService {
private paymentRepo: PaymentRepository;
constructor() {
this.paymentRepo = new PaymentRepository();
}
/**
* Detecta actividad sospechosa de pagos
*/
async detectFraud(params: {
user_id: string;
amount: number;
ip_address?: string;
}): Promise<{ is_suspicious: boolean; reasons: string[] }> {
const reasons: string[] = [];
// 1. Verificar múltiples pagos fallidos
const failedPayments = await this.paymentRepo.getRecentFailedPayments(
params.user_id,
3600 // última hora
);
if (failedPayments.length >= 3) {
reasons.push('Multiple failed payment attempts');
}
// 2. Verificar monto inusualmente alto
const avgPayment = await this.paymentRepo.getAveragePaymentAmount(params.user_id);
if (avgPayment > 0 && params.amount > avgPayment * 10) {
reasons.push('Unusually high payment amount');
}
// 3. Velocity check - múltiples pagos en corto tiempo
const recentPayments = await this.paymentRepo.getRecentPayments(
params.user_id,
1800 // últimos 30 min
);
if (recentPayments.length >= 5) {
reasons.push('Too many payments in short time');
}
// 4. Verificar cambios frecuentes de payment method
const recentMethods = await this.paymentRepo.getRecentPaymentMethodChanges(
params.user_id,
86400 // último día
);
if (recentMethods.length >= 3) {
reasons.push('Frequent payment method changes');
}
const is_suspicious = reasons.length > 0;
if (is_suspicious) {
logger.warn('Suspicious payment activity detected', {
user_id: params.user_id,
reasons,
amount: params.amount,
});
}
return { is_suspicious, reasons };
}
/**
* Verifica si usuario está en lista de bloqueo
*/
async isBlocked(userId: string): Promise<boolean> {
// Implementar lógica de lista negra
// Puede usar Redis o tabla en DB
return false;
}
/**
* Bloquea usuario temporalmente
*/
async blockUser(userId: string, reason: string, durationSeconds: number): Promise<void> {
logger.warn('User blocked from payments', {
user_id: userId,
reason,
duration: durationSeconds,
});
// Guardar en Redis con TTL
// await redis.setex(`blocked:${userId}`, durationSeconds, reason);
}
}
4.4 Audit Logger
// src/services/security/payment-audit.service.ts
import { logger } from '../../utils/logger';
export enum PaymentAuditAction {
PAYMENT_INITIATED = 'PAYMENT_INITIATED',
PAYMENT_COMPLETED = 'PAYMENT_COMPLETED',
PAYMENT_FAILED = 'PAYMENT_FAILED',
REFUND_REQUESTED = 'REFUND_REQUESTED',
REFUND_COMPLETED = 'REFUND_COMPLETED',
SUBSCRIPTION_CREATED = 'SUBSCRIPTION_CREATED',
SUBSCRIPTION_CANCELED = 'SUBSCRIPTION_CANCELED',
PAYMENT_METHOD_ADDED = 'PAYMENT_METHOD_ADDED',
PAYMENT_METHOD_REMOVED = 'PAYMENT_METHOD_REMOVED',
FRAUD_DETECTED = 'FRAUD_DETECTED',
}
interface PaymentAuditEntry {
action: PaymentAuditAction;
user_id: string;
amount?: number;
payment_id?: string;
ip_address?: string;
user_agent?: string;
metadata?: Record<string, any>;
}
export class PaymentAuditService {
log(entry: PaymentAuditEntry): void {
logger.info('PAYMENT_AUDIT', {
timestamp: new Date().toISOString(),
action: entry.action,
user_id: entry.user_id,
amount: entry.amount,
payment_id: entry.payment_id,
ip_address: entry.ip_address,
user_agent: entry.user_agent,
metadata: entry.metadata,
});
// Opcionalmente guardar en tabla de auditoría
}
}
5. Frontend Security
5.1 Stripe.js Integration
// CORRECTO: Usar Stripe Elements
import { CardElement } from '@stripe/react-stripe-js';
const PaymentForm = () => {
const stripe = useStripe();
const elements = useElements();
const handleSubmit = async () => {
const cardElement = elements.getElement(CardElement);
// Crear token con Stripe.js (datos nunca pasan por nuestro servidor)
const { token, error } = await stripe.createToken(cardElement);
if (token) {
// Enviar solo token al backend
await api.post('/payments', { token: token.id });
}
};
return <CardElement />;
};
// INCORRECTO: NUNCA hacer esto
const BadPaymentForm = () => {
const [cardNumber, setCardNumber] = useState('');
const [cvv, setCvv] = useState('');
// ❌ NUNCA capturar datos de tarjeta directamente
return (
<form>
<input
type="text"
value={cardNumber}
onChange={(e) => setCardNumber(e.target.value)}
/>
<input
type="text"
value={cvv}
onChange={(e) => setCvv(e.target.value)}
/>
</form>
);
};
6. Security Checklist
6.1 PCI DSS Compliance Checklist
- Usar solo Stripe.js/Elements para captura de tarjetas
- NUNCA almacenar CVV/CVC
- NUNCA almacenar datos completos de tarjeta
- Solo guardar tokens de Stripe
- Usar HTTPS en todos los endpoints
- Validar firma de webhooks de Stripe
- Implementar rate limiting
- Logs de auditoría para todas las transacciones
- Detección de fraude básica
- Encriptación de datos en tránsito (TLS 1.2+)
- Acceso restringido a datos de pagos (RBAC)
- Monitoreo de actividad sospechosa
- Política de contraseñas fuertes
- Autenticación de dos factores para admin
6.2 Development Checklist
- Variables de entorno seguras
- Secrets no en código fuente
- Test mode keys para desarrollo
- Production keys solo en producción
- Webhook signatures verificadas
- Error messages sin información sensible
- Input validation en todos los endpoints
- XSS protection
- CSRF protection
- SQL injection prevention
7. Incident Response
7.1 Procedimiento de Incidente
-
Detección
- Monitoreo de logs
- Alertas automáticas
- Reportes de usuarios
-
Contención
- Bloquear usuario afectado
- Pausar procesos automáticos
- Aislar sistemas comprometidos
-
Investigación
- Analizar logs de auditoría
- Identificar alcance
- Documentar hallazgos
-
Recuperación
- Revertir cambios no autorizados
- Restaurar desde backup si necesario
- Verificar integridad de datos
-
Post-Mortem
- Documentar incidente
- Implementar mejoras
- Actualizar procedimientos
8. Monitoring y Alertas
8.1 Métricas Clave
// Alertas automáticas
const ALERT_THRESHOLDS = {
FAILED_PAYMENTS_PER_HOUR: 10,
HIGH_VALUE_TRANSACTION: 10000,
REFUND_RATE_PERCENTAGE: 5,
WEBHOOK_FAILURE_RATE: 0.1,
};
// Monitorear
- Tasa de pagos fallidos
- Volumen de reembolsos
- Tiempo de respuesta de Stripe
- Errores de webhook
- Intentos de fraude detectados
9. Configuración
# Security
STRIPE_SECRET_KEY=sk_live_... # Nunca sk_test_ en producción
STRIPE_WEBHOOK_SECRET=whsec_...
# Encryption
ENCRYPTION_KEY=32-character-secure-key
# Rate Limiting
PAYMENT_RATE_LIMIT_PER_HOUR=10
REFUND_RATE_LIMIT_PER_DAY=3
# Fraud Detection
FRAUD_DETECTION_ENABLED=true
MAX_PAYMENT_AMOUNT=10000
10. Testing
// tests/security/pci-compliance.test.ts
describe('PCI Compliance', () => {
it('should reject card data in request body', async () => {
const response = await request(app)
.post('/api/v1/payments')
.send({
card_number: '4242424242424242',
cvv: '123',
});
expect(response.status).toBe(400);
expect(response.body.error).toContain('tokenization');
});
it('should only accept Stripe tokens', async () => {
const response = await request(app)
.post('/api/v1/payments')
.send({
payment_method_id: 'pm_1234567890',
amount: 100,
});
expect(response.status).not.toBe(400);
});
});