Seguridad de APIs

Infraestructura Y Gobernanza Security Notes Jan 9, 2025 JAVASCRIPT
security authentication authorization encryption owasp

Definición

La seguridad de APIs es la disciplina integral de proteger APIs contra accesos no autorizados, filtraciones de datos, ataques de inyección, abuso y otras amenazas durante todo su ciclo de vida - desde el diseño y desarrollo hasta el despliegue y retiro. Abarca autenticación (demostrar quién eres), autorización (qué tienes permitido hacer), cifrado (proteger datos en tránsito y en reposo), validación de entrada, limitación de tasa, monitoreo y respuesta a incidentes. A diferencia de la seguridad de aplicaciones web, la seguridad de APIs debe manejar comunicación máquina-a-máquina, autenticación basada en tokens y ataques programáticos a escala.

El desafío de la seguridad de APIs es que las APIs están diseñadas para ser accesibles - a menudo públicamente - haciéndolas objetivos principales para atacantes. Mientras que un sitio web podría tener un formulario de login amigable con CAPTCHA, las APIs deben servir millones de peticiones automatizadas por segundo. Esto crea una superficie de ataque masiva donde las vulnerabilidades pueden ser explotadas a velocidad de máquina. Un solo endpoint de autenticación roto puede filtrar millones de registros en minutos.

La seguridad de APIs no es una sola herramienta o técnica; es una estrategia de defensa en capas. Necesitas autenticación para probar identidad, autorización para aplicar permisos, cifrado para proteger datos sensibles, limitación de tasa para prevenir abuso, validación de entrada para detener ataques de inyección, logging para detectar brechas, y respuesta a incidentes para contener daños. Omite cualquier capa y creas una debilidad explotable. El OWASP API Security Top 10 lista las vulnerabilidades más críticas que las organizaciones deben abordar.

Ejemplo

Broken Object Level Authorization (BOLA): La API de Uber permitía a cualquier usuario autenticado acceder a cualquier recibo de viaje cambiando el ID del viaje en la URL (/api/trips/12345). Los atacantes enumeraron millones de IDs, recolectando recibos que contenían nombres, direcciones y números de teléfono. Solución: Verificar que req.user.id === trip.user_id antes de devolver datos.

Autenticación Rota: La API de Peloton exponía datos de usuario a través de un endpoint no autenticado. Cualquiera podía acceder a detalles de cuenta, historial de entrenamientos e información de perfil privado sin iniciar sesión. Solución: Requerir autenticación en todos los endpoints no públicos y validar tokens en cada petición.

Exposición Excesiva de Datos: La Graph API de Facebook devolvía objetos de usuario completos (email, teléfono, lista de amigos) incluso cuando el cliente solo solicitaba nombre y foto de perfil. Los atacantes recolectaron conjuntos masivos de datos haciendo peticiones mínimas. Solución: Implementar autorización a nivel de campo y devolver solo campos solicitados.

Falta de Limitación de Tasa: La API de Parler no tenía límites de tasa, permitiendo a investigadores hacer scraping del 99% de publicaciones (70TB de datos) incluyendo contenido eliminado y ubicaciones GPS. Solución: Implementar limitación de tasa por usuario, por IP y por endpoint con diferentes límites para operaciones de lectura vs. escritura.

Asignación Masiva: La API de GitHub una vez permitió a usuarios escalar privilegios agregando "admin": true a su petición de actualización de perfil. La API aceptaba ciegamente todos los campos JSON sin validación. Solución: Usar listas blancas para campos aceptados y nunca confiar en entrada del cliente.

Inyección SQL: Una API de comercio electrónico con funcionalidad de búsqueda: /api/products?query=laptop era vulnerable porque insertaba directamente el parámetro query en SQL. Los atacantes inyectaban ' OR 1=1-- para volcar toda la base de datos. Solución: Usar consultas parametrizadas o ORMs que escapen entradas.

Analogía

La Defensa del Castillo Medieval: Un castillo tiene múltiples capas de seguridad - foso (firewall de red), muros (autenticación), puertas con guardias (autorización), torres de vigilancia (monitoreo), y bóvedas para tesoros (cifrado). Los atacantes deben romper cada capa. La seguridad de APIs funciona igual: WAF → autenticación → autorización → validación de entrada → cifrado → logging. Rompe una capa, enfrenta la siguiente.

El Sistema de Seguridad Aeroportuaria: Para abordar un avión, necesitas ID (autenticación), un boleto válido (autorización), revisión de equipaje (validación de entrada), y detectores de metal (detección de amenazas). El personal del aeropuerto monitorea cámaras (logging) y responde a incidentes. Ningún punto de control es suficiente; necesitas todas las capas trabajando juntas. Eso es seguridad de APIs.

La Bóveda del Banco: Los bancos no solo cierran la bóveda (cifrado). Tienen guardias armados (autenticación), listas de control de acceso (autorización), cámaras por todas partes (logging), alarmas ante actividad inusual (detección de anomalías), y cerraduras con retardo de tiempo (limitación de tasa). La seguridad de APIs requiere el mismo enfoque multicapa.

El Casco del Submarino: Un submarino tiene un casco exterior, casco interior, y secciones compartimentadas. Si un área se inunda, las puertas estancas la sellan (circuit breakers). La seguridad de APIs usa defensa en profundidad similar: si la autenticación falla, la autorización lo detecta. Si la autorización falla, el cifrado limita el daño.

Ejemplo de Código

// Implementación integral de seguridad de APIs
import express from 'express';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import jwt from 'jsonwebtoken';
import { body, validationResult } from 'express-validator';
import crypto from 'crypto';

const app = express();

// 1. Cabeceras de Seguridad (Helmet)
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"]
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));

// 2. Limitación de Tasa
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutos
  max: 100, // limita cada IP a 100 peticiones por windowMs
  message: 'Demasiadas peticiones desde esta IP, intenta de nuevo más tarde.',
  standardHeaders: true,
  legacyHeaders: false
});
app.use('/api/', limiter);

// 3. Middleware de Autenticación
function authenticate(req, res, next) {
  const token = req.headers.authorization?.replace('Bearer ', '');

  if (!token) {
    return res.status(401).json({ error: 'Token de autenticación faltante' });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(401).json({ error: 'Token inválido o expirado' });
  }
}

// 4. Middleware de Autorización (RBAC)
function authorize(...allowedRoles) {
  return (req, res, next) => {
    if (!req.user || !allowedRoles.includes(req.user.role)) {
      return res.status(403).json({
        error: 'Prohibido',
        message: 'No tienes permiso para acceder a este recurso'
      });
    }
    next();
  };
}

// 5. Autorización a Nivel de Objeto (Prevenir BOLA)
async function checkOwnership(req, res, next) {
  const resourceId = req.params.id;
  const resource = await db.findById(resourceId);

  if (!resource) {
    return res.status(404).json({ error: 'Recurso no encontrado' });
  }

  // Crítico: Verificar propiedad
  if (resource.userId !== req.user.id && req.user.role !== 'admin') {
    return res.status(403).json({
      error: 'Prohibido',
      message: 'Solo puedes acceder a tus propios recursos'
    });
  }

  req.resource = resource;
  next();
}

// 6. Validación y Sanitización de Entrada
const validateUser = [
  body('email').isEmail().normalizeEmail(),
  body('username').isLength({ min: 3, max: 20 }).trim().escape(),
  body('age').optional().isInt({ min: 13, max: 120 })
];

// 7. Ejemplo de Endpoint Seguro
app.post('/api/users',
  authenticate,
  authorize('admin'),
  validateUser,
  async (req, res) => {
    // Verificar resultados de validación
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    // Protección contra asignación masiva: lista blanca
    const allowedFields = ['email', 'username', 'age'];
    const userData = {};
    allowedFields.forEach(field => {
      if (req.body[field] !== undefined) {
        userData[field] = req.body[field];
      }
    });

    try {
      const user = await db.createUser(userData);

      // No exponer campos sensibles
      const { password, ...safeUser } = user;

      res.status(201).json(safeUser);
    } catch (err) {
      // No filtrar errores internos
      res.status(500).json({ error: 'Error interno del servidor' });
      logger.error('Creación de usuario falló', err);
    }
  }
);

// 8. Acceso a Recursos con Protección BOLA
app.get('/api/invoices/:id',
  authenticate,
  checkOwnership,
  async (req, res) => {
    // req.resource ya validado por middleware checkOwnership
    res.json(req.resource);
  }
);

// 9. Logging de Auditoría
function auditLog(req, res, next) {
  const logEntry = {
    timestamp: new Date().toISOString(),
    userId: req.user?.id,
    method: req.method,
    path: req.path,
    ip: req.ip,
    userAgent: req.headers['user-agent']
  };

  // Registrar en almacenamiento seguro (no consola en producción)
  logger.info('Petición API', logEntry);
  next();
}

app.use(auditLog);

// 10. Manejo de Errores (No filtrar información)
app.use((err, req, res, next) => {
  // Registrar error completo internamente
  logger.error('Error no manejado', err);

  // Enviar error sanitizado al cliente
  res.status(err.status || 500).json({
    error: 'Ocurrió un error',
    message: process.env.NODE_ENV === 'development' ? err.message : 'Por favor intenta de nuevo más tarde'
  });
});

Diagrama

graph TB
    subgraph Client["Aplicación Cliente"]
        C[Petición API]
    end

    subgraph Edge["Protección Edge"]
        WAF[Web Application Firewall
Protección DDoS] CDN[CDN / API Gateway
Filtrado Geográfico] end subgraph Gateway["Capa API Gateway"] RL[Limitación de Tasa
100 req/seg por IP] TLS[Terminación TLS
Validación de Certificado] end subgraph Auth["Autenticación y Autorización"] A1[Autenticación
Verificar JWT/API Key] A2[Autorización
Verificar Permisos RBAC] A3[Auth Nivel Objeto
Verificar Propiedad Recurso] end subgraph App["Capa de Aplicación"] V[Validación de Entrada
Validación de Esquema] B[Lógica de Negocio
Límites de Tasa, Cuotas] E[Cifrado
Datos Sensibles] end subgraph Data["Capa de Datos"] DB[(Base de Datos
Cifrada en Reposo)] AuditLog[(Logs de Auditoría
Inmutables)] end subgraph Monitor["Monitoreo y Respuesta"] M[SIEM / Monitoreo
Detección de Anomalías] IR[Respuesta a Incidentes
Bloqueo Automático] end C --> WAF WAF --> CDN CDN --> RL RL --> TLS TLS --> A1 A1 -->|Token Válido| A2 A1 -->|Inválido| X1[401 No Autorizado] A2 -->|Autorizado| A3 A2 -->|Prohibido| X2[403 Prohibido] A3 -->|Propietario Coincide| V A3 -->|Intento BOLA| X3[403 BOLA Bloqueado] V -->|Entrada Válida| B V -->|Entrada Inválida| X4[400 Petición Inválida] B --> E E --> DB E --> AuditLog DB --> M AuditLog --> M M --> IR IR -.Auto-Bloqueo.-> WAF style WAF fill:#ffe6e6 style A1 fill:#e6f3ff style A2 fill:#e6f3ff style A3 fill:#e6f3ff style V fill:#fffbe6 style E fill:#e6ffe6 style M fill:#f3e6ff style X1 fill:#ffcccc style X2 fill:#ffcccc style X3 fill:#ffcccc style X4 fill:#ffcccc

Notas de Seguridad

SECURITY NOTES

CRÍTICO - …

Configuración y Validación:

  • La seguridad de APIs requiere defensa en profundidad con múltiples capas.
  • Siempre usa HTTPS/TLS para todo el tráfico de API; nunca envíes datos sensibles sobre HTTP.
  • Implementa autenticación en cada endpoint no público usando protocolos estándar de la industria (OAuth 2.0, JWT, API keys).
  • Valida tokens en cada petición; nunca confíes en tokens expirados o no verificados.
  • Implementa autorización granular verificando tanto permisos basados en roles (RBAC) como a nivel de objeto (protección BOLA).
  • Valida y sanitiza TODAS las entradas usando listas blancas, no listas negras.
  • Usa consultas parametrizadas u ORMs para prevenir inyección SQL.
  • Implementa limitación de tasa en múltiples niveles: global, por usuario, por IP, por endpoint.
  • Registra todos los eventos de seguridad (autenticación fallida, denegaciones de permiso, patrones inusuales) en almacenamiento inmutable.
  • Monitorea logs para patrones de ataque usando herramientas SIEM.
  • Implementa respuesta automática a incidentes para firmas de ataque conocidas.

Monitoreo y Protección:

  • Nunca expongas detalles de error internos o trazas de pila a clientes.
  • Usa cabeceras de seguridad (Helmet.js) para prevenir XSS, clickjacking y otros ataques basados en navegador.
  • Rota secretos regularmente (API keys, secretos JWT, credenciales de base de datos).
  • Implementa versionado de API para permitir parches de seguridad sin romper clientes.
  • Realiza auditorías de seguridad y pruebas de penetración regularmente.
  • Sigue las directrices OWASP API Security Top 10.
  • Implementa el principio de privilegio mínimo: da los permisos mínimos requeridos.
  • Usa almacenamiento cifrado para datos sensibles en reposo.
  • Implementa políticas CORS cuidadosamente para prevenir orígenes no autorizados.
  • Nunca confíes en validación del lado del cliente; siempre valida del lado del servidor.
  • Usa escáneres de seguridad en pipelines CI/CD para detectar vulnerabilidades antes del despliegue.

Mejores Prácticas

  1. OWASP API Security Top 10: Aborda las 10 categorías: Broken Object Level Authorization, Broken User Authentication, Excessive Data Exposure, Lack of Resources & Rate Limiting, Broken Function Level Authorization, Mass Assignment, Security Misconfiguration, Injection, Improper Assets Management, Insufficient Logging & Monitoring.

  2. Defensa en Profundidad: Capas de controles de seguridad (WAF → limitación de tasa → autenticación → autorización → validación de entrada → cifrado → logging). Si una falla, otras detectan el ataque.

  3. Arquitectura Zero Trust: Nunca confíes, siempre verifica. Autentica y autoriza cada petición, incluso de servicios internos. No asumas que la ubicación de red implica confianza.

  4. Principio de Privilegio Mínimo: Da a usuarios/servicios los permisos mínimos necesarios. Denegar por defecto, permitir explícitamente. Audita permisos regularmente.

  5. Seguro por Defecto: Los nuevos endpoints deben requerir autenticación por defecto. Opta por acceso público, no salir de la seguridad.

  6. Testing de Seguridad en CI/CD: Ejecuta escaneos automáticos de seguridad (SAST, DAST, escaneo de dependencias) en cada commit. Bloquea despliegues que fallan verificaciones de seguridad.

  7. Patrón API Gateway: Centraliza controles de seguridad (auth, limitación de tasa, logging) en un API gateway. No dupliques lógica de seguridad entre microservicios.

  8. Auditorías de Seguridad Regulares: Realiza pruebas de penetración trimestralmente. Revisa cumplimiento OWASP Top 10 anualmente. Monitorea bases de datos CVE para vulnerabilidades de dependencias.

Errores Comunes

Autenticación Sin Autorización: Verificar identidad pero no verificar permisos. El Usuario A puede acceder a datos del Usuario B porque la API solo verifica si la petición está autenticada, no autorizada.

Seguridad del Lado del Cliente: Confiar en entrada del cliente o validación del lado del cliente. Los atacantes controlan el cliente y pueden omitir todas las verificaciones del lado del cliente.

Secretos Hardcodeados: Hacer commit de API keys, secretos JWT, o contraseñas de base de datos en control de versiones. Usa variables de entorno y servicios de gestión de secretos.

Sin Limitación de Tasa: Permitir peticiones ilimitadas habilita ataques DDoS, fuerza bruta y scraping. Siempre implementa límites de tasa.

Exposición Excesiva de Datos: Devolver objetos completos de base de datos cuando los clientes solo necesitan campos específicos. Usa DTOs (Data Transfer Objects) y autorización a nivel de campo.

Vulnerabilidades de Asignación Masiva: Aceptar todos los campos JSON sin validación. Los atacantes agregan "role": "admin" para escalada de privilegios.

Ignorar Métodos HTTP: Tratar GET igual que POST. GET nunca debe mutar estado; aplica reglas de idempotencia.

Sin Logging: Sin logs de auditoría, no puedes detectar brechas o investigar incidentes. Registra autenticación, fallos de autorización y operaciones sensibles.

Confiar en Redes Internas: Asumir que APIs internas no necesitan seguridad porque están “detrás del firewall.” Las redes modernas son planas; asume brecha.

Gestión de Endpoints Obsoletos: Dejar versiones antiguas de API ejecutándose indefinidamente crea deuda de seguridad. Implementa gestión de ciclo de vida de API con cronogramas de sunset.

Estándares y RFCs

Términos Relacionados