Volver al Blog
Security 13 de febrero de 2026 6 min de lectura

Seguridad en APIs REST

Mejores prácticas para proteger tus APIs: autenticación JWT, rate limiting y prevención de vulnerabilidades comunes.

Security JWT 🔌 API
Neiser Custodio

Neiser Custodio

Desarrollador FullStack

Seguridad en APIs REST

El panorama de seguridad en APIs 2026

Las APIs se han convertido en la columna vertebral de las aplicaciones modernas, pero también son el objetivo principal de los atacantes. En 2026, asegurar APIs no es opcional - es mission-critical. Cada endpoint expuesto es una puerta potencial para atacantes que buscan explotar vulnerabilidades.

Según reportes de seguridad de 2025, las APIs representan más del 80% del tráfico web, y los ataques dirigidos a APIs han aumentado un 200% en los últimos dos años. La superficie de ataque se ha expandido dramáticamente con microservicios, arquitecturas serverless y edge computing.

Autenticación vs Autorización: La diferencia crucial

Estos conceptos frecuentemente se confunden, pero resuelven problemas diferentes:

Autenticación

Pregunta: ¿Quién eres?

Verifica la identidad del usuario o sistema antes de procesar cualquier solicitud. Establece confianza antes de permitir acceso.

Autorización

Pregunta: ¿Qué puedes hacer?

Define qué recursos puede acceder un usuario autenticado. Controla permisos y previene escalación de privilegios.

Error común

Autenticar sin autorizar adecuadamente. Un usuario autenticado no debería poder acceder a recursos de otros usuarios solo por estar logueado.

JWT: El estándar moderno para APIs stateless

JSON Web Tokens (JWT) se han consolidado como el método preferido para autenticación en APIs modernas, especialmente en arquitecturas distribuidas y microservicios.

Estructura de un JWT

Un JWT consta de tres partes separadas por puntos:

Header.Payload.Signature

  • Header: Tipo de token y algoritmo de firma
  • Payload: Claims (datos del usuario y metadata)
  • Signature: Garantiza integridad del token

Por qué JWT en 2026

Ventajas clave:

  • Stateless: No requiere almacenamiento de sesión en servidor
  • Escalable: Perfecto para arquitecturas distribuidas y microservicios
  • Portable: Funciona across dominios y servicios
  • Performante: Validación sin consultas a base de datos

JWT en acción

Después de autenticarse, el servidor genera un JWT firmado. El cliente incluye este token en el header Authorization: Bearer <token> en requests subsecuentes. El servidor valida la firma sin consultar base de datos.

Mejores prácticas JWT para 2026

1. Usa algoritmos asimétricos (RS256 vs HS256)

RS256 (Recomendado):

  • Clave privada firma, clave pública verifica
  • Los servicios pueden verificar sin poder falsificar tokens
  • Ideal para microservicios

HS256 (Usar con precaución):

  • Misma clave para firmar y verificar
  • Cualquiera que puede verificar también puede falsificar
  • Solo para sistemas simples donde el verificador es confiable

2. Tokens de corta duración + Refresh Tokens


  // Access token: 15 minutos
  const accessToken = jwt.sign(
    { userId: user.id, role: user.role },
    privateKey,
    { expiresIn: '15m', algorithm: 'RS256' }
  )

  // Refresh token: 7 días (almacenado en httpOnly cookie)
  const refreshToken = jwt.sign(
    { userId: user.id, type: 'refresh' },
    refreshSecret,
    { expiresIn: '7d' }
  )

Estrategia:

  • Access tokens cortos minimizan daño si son robados
  • Refresh tokens permiten obtener nuevos access tokens
  • Almacena refresh tokens en httpOnly cookies para prevenir XSS

3. Valida TODOS los claims relevantes

No solo verifiques la firma - valida:


  const decoded = jwt.verify(token, publicKey, {
    algorithms: ['RS256'],
    issuer: 'api.tuapp.com',
    audience: 'tuapp-frontend'
  })

  // Valida claims adicionales
  if (Date.now() >= decoded.exp * 1000) {
    throw new Error('Token expirado')
  }

  if (decoded.nbf && Date.now() < decoded.nbf * 1000) {
    throw new Error('Token aún no válido')
  }

4. Nunca almacenes datos sensibles en el payload

El payload es Base64-encoded, NO encriptado. Cualquiera puede decodificarlo.

❌ Nunca incluyas:

  • Passwords
  • Números de tarjeta de crédito
  • PII (Personally Identifiable Information)
  • Secrets o API keys

✅ Incluye solo:

  • User ID
  • Roles/permisos
  • Issuer y audience
  • Timestamps (exp, nbf, iat)

Alternativa PASETO

Platform-Agnostic Security Tokens (PASETO) elimina confusión de algoritmos y proporciona mejor seguridad por defecto. Sin embargo, JWT mantiene adopción masiva y mejor tooling en 2026.

OAuth 2.0 y OpenID Connect

Para aplicaciones que requieren delegated access o integraciones third-party, OAuth 2.0 es el framework estándar.

OAuth 2.0: Delegated Authorization

Permite a usuarios otorgar acceso a sus recursos sin compartir credenciales.

Flujos comunes en 2026:

  1. Authorization Code Flow (+ PKCE): Para web y mobile apps
  2. Client Credentials Flow: Para machine-to-machine
  3. Device Code Flow: Para dispositivos con input limitado

OpenID Connect: Authentication sobre OAuth 2.0

OIDC añade una capa de identidad sobre OAuth 2.0, proporcionando ID tokens además de access tokens.

Cuándo usar:

  • Login social (Google, GitHub, etc.)
  • Single Sign-On (SSO) empresarial
  • Federación de identidad

Rate Limiting: Tu primera línea de defensa

El rate limiting previene abuso, ataques DDoS, y scraping automatizado.

Estrategias de Rate Limiting

1. Fixed Window


  Límite: 100 requests por minuto
  Simple pero permite bursts al inicio de ventanas

2. Sliding Window


  Más justo, cuenta requests en ventana móvil
  Previene gaming del sistema
  

3. Token Bucket


  Permite bursts temporales
  Refill gradual de tokens

Implementación con Redis


  import Redis from 'ioredis'
  const redis = new Redis()

  async function rateLimit(userId: string): Promise<boolean> {
    const key = `rate_limit:${userId}`
    const limit = 100
    const window = 60 // segundos
    
    const current = await redis.incr(key)
    
    if (current === 1) {
      await redis.expire(key, window)
    }
    
    return current <= limit
  }

  // En tu middleware
  app.use(async (req, res, next) => {
    const allowed = await rateLimit(req.user.id)
    
    if (!allowed) {
      return res.status(429).json({
        error: 'Too Many Requests',
        retryAfter: 60
      })
    }
    
    next()
  })

Rate limiting por endpoint

Implementa límites diferentes según criticidad del endpoint. Login endpoints deben tener límites más estrictos que endpoints de lectura.

RBAC y ABAC: Modelos de autorización

Role-Based Access Control (RBAC)

Asigna permisos basados en roles predefinidos.

Ejemplo:


  enum Role {
    ADMIN = 'admin',
    EDITOR = 'editor',
    VIEWER = 'viewer'
  }

  const permissions = {
    admin: ['read', 'write', 'delete'],
    editor: ['read', 'write'],
    viewer: ['read']
  }

  function authorize(userRole: Role, action: string): boolean {
    return permissions[userRole]?.includes(action) ?? false
  }

Fortalezas:

  • Simple y fácil de auditar
  • Perfecto para jerarquías estables
  • Compliance-friendly

Attribute-Based Access Control (ABAC)

Decisiones basadas en atributos dinámicos (usuario, recurso, contexto).

Ejemplo:


  function canAccessDocument(user, document, context) {
    return (
      user.id === document.ownerId ||
      user.department === document.department &&
      context.ipAddress.startsWith('10.') // Red interna
    )
  }

Fortalezas:

  • Extremadamente flexible
  • Políticas complejas y dinámicas
  • Contextual (ubicación, tiempo, dispositivo)

Vulnerabilidades OWASP API Top 10 (2023)

API1: Broken Object Level Authorization

Problema: APIs exponen endpoints que manejan object IDs, permitiendo acceso no autorizado modificando el ID.

  
  // ❌ Vulnerable
  app.get('/api/users/:id/profile', (req, res) => {
    const profile = db.getProfile(req.params.id)
    res.json(profile)
  })

  // ✅ Seguro
  app.get('/api/users/:id/profile', authenticate, (req, res) => {
    if (req.user.id !== req.params.id && !req.user.isAdmin) {
      return res.status(403).json({ error: 'Forbidden' })
    }
    const profile = db.getProfile(req.params.id)
    res.json(profile)
  })

API2: Broken Authentication

Prevención:

  • Implementa MFA where possible
  • Usa contraseñas fuertes (bcrypt, argon2)
  • Implementa account lockout después de fallos
  • Usa CAPTCHA en login después de intentos fallidos

API3: Broken Object Property Level Authorization

Problema: APIs returning o aceptando propiedades sensibles sin validar autorización.


  // ❌ Vulnerable - expone campos sensibles
  app.get('/api/users/:id', (req, res) => {
    const user = db.getUser(req.params.id)
    res.json(user) // Incluye password hash, SSN, etc
  })

  // ✅ Seguro - filtra campos según role
  app.get('/api/users/:id', authenticate, (req, res) => {
    const user = db.getUser(req.params.id)
    const safeFields = req.user.isAdmin 
      ? user 
      : { id: user.id, name: user.name, email: user.email }
    res.json(safeFields)
  })

API4: Unrestricted Resource Consumption

Solución: Rate limiting + validación de input size


  app.use(express.json({ limit: '10kb' })) // Limita body size

  app.post('/api/search', rateLimit, async (req, res) => {
    const { query, limit = 10 } = req.body
    
    // Valida y sanitiza
    if (limit > 100) {
      return res.status(400).json({ error: 'Limit too large' })
    }
    
    const results = await db.search(query, limit)
    res.json(results)
  })

Mass Assignment Attack

Nunca confíes ciegamente en request body. Define explícitamente qué campos son permitidos actualizar.

Seguridad en transmisión

HTTPS everywhere

En 2026, HTTPS no es opcional. Usa TLS 1.3 como mínimo.

Configuración recomendada:

  • Certificate from Let’s Encrypt (gratis y automático)
  • HSTS header (Strict-Transport-Security)
  • Redirect HTTP a HTTPS automáticamente

CORS configurado correctamente


  app.use(cors({
    origin: ['https://tuapp.com', 'https://admin.tuapp.com'],
    credentials: true,
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    allowedHeaders: ['Content-Type', 'Authorization']
  }))

Nunca uses origin: '*' con credentials: true en producción.

Headers de seguridad


  import helmet from 'helmet'

  app.use(helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        scriptSrc: ["'self'"],
        imgSrc: ["'self'", 'data:', 'https:']
      }
    },
    hsts: {
      maxAge: 31536000,
      includeSubDomains: true,
      preload: true
    }
  }))

Input Validation y Sanitization

Valida y sanitiza TODOS los inputs del usuario.


  import { z } from 'zod'

  const CreateUserSchema = z.object({
    email: z.string().email().max(255),
    password: z.string().min(12).max(128),
    name: z.string().min(1).max(100).regex(/^[a-zA-Z\s]+$/)
  })

  app.post('/api/users', async (req, res) => {
    try {
      const validated = CreateUserSchema.parse(req.body)
      // Procesar validated data
    } catch (error) {
      return res.status(400).json({ error: error.errors })
    }
  })

Logging y Monitoring

Implementa logging comprehensivo para detectar ataques:


  logger.info('API Request', {
    userId: req.user?.id,
    method: req.method,
    path: req.path,
    ip: req.ip,
    userAgent: req.get('user-agent'),
    responseTime: Date.now() - req.startTime,
    statusCode: res.statusCode
  })

Alertas para:

  • Múltiples intentos de login fallidos
  • Requests a endpoints no existentes (scanning)
  • Cambios de IP sospechosos
  • Patrones de rate limit violations

Zero Trust Architecture

En 2026, adopta principios zero trust: nunca confíes, siempre verifica. Incluso tráfico interno debe ser autenticado y autorizado.

Conclusión

La seguridad en APIs no es un checklist único - es un proceso continuo de evaluación, implementación y monitoreo. Las amenazas evolucionan, y tus defensas también deben hacerlo.

Prioridades para 2026:

  1. Implementa JWT con algoritmos asimétricos y tokens de corta duración
  2. Rate limiting agresivo en todos los endpoints
  3. Validación exhaustiva de inputs con schemas
  4. Autorización granular (RBAC o ABAC según complejidad)
  5. HTTPS + headers de seguridad apropiados
  6. Logging comprehensivo y alertas automáticas
  7. Auditorías de seguridad regulares

Recuerda: un solo endpoint vulnerable puede comprometer todo tu sistema. La seguridad debe ser diseñada desde el inicio, no añadida después como parche.

Neiser Custodio

Neiser Custodio

Desarrollador FullStack

Me apasiona crear soluciones web escalables y compartir conocimientos a través de publicaciones de blog enriquecedoras.