Seguridad en APIs REST
Mejores prácticas para proteger tus APIs: autenticación JWT, rate limiting y prevención de vulnerabilidades comunes.
Neiser Custodio
Desarrollador FullStack
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:
- Authorization Code Flow (+ PKCE): Para web y mobile apps
- Client Credentials Flow: Para machine-to-machine
- 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:
- Implementa JWT con algoritmos asimétricos y tokens de corta duración
- Rate limiting agresivo en todos los endpoints
- Validación exhaustiva de inputs con schemas
- Autorización granular (RBAC o ABAC según complejidad)
- HTTPS + headers de seguridad apropiados
- Logging comprehensivo y alertas automáticas
- 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.