Seguridad en REST APIs: Autenticación, Autorización y Medidas de Protección
La seguridad en REST APIs garantiza que solo clientes autorizados accedan a recursos permitidos. Quien construye APIs debe entender autenticación y autorización: son el fundamento de cualquier interfaz segura.
¿Qué es la seguridad en REST APIs?
La seguridad en REST APIs comprende todas las medidas que protegen una API RESTful contra acceso no autorizado, manipulación y ataques. Se sustenta en dos pilares centrales:
Autenticación responde: ¿Quién eres? El cliente prueba su identidad mediante tokens, claves API, certificados o cookies de sesión.
Autorización responde: ¿Puedes hacerlo? Tras autenticar al cliente, se verifica si tiene permisos para ejecutar la acción solicitada.
Esta separación es esencial. Un usuario autenticado no está autorizado automáticamente a hacer cualquier cosa. Un usuario puede leer sus propios datos, pero no los ajenos. Un administrador tiene más permisos que un editor. Este principio se implementa mediante modelos como RBAC (Role-Based Access Control) o ABAC (Attribute-Based Access Control).
¿Quién utiliza seguridad en REST APIs?
- Desarrolladores backend implementan middleware de autenticación y lógica de autorización
- Diseñadores de APIs definen qué protección necesita cada endpoint
- Ingenieros DevOps configuran TLS, rate limiting y API gateways
- Ingenieros de seguridad auditan APIs en busca de vulnerabilidades
- Desarrolladores frontend deben enviar tokens de autenticación correctamente
¿Por qué es importante este tema en informática y en exámenes?
El Top 10 de OWASP enumera Broken Access Control en el puesto 1 y Cryptographic Failures en el 2, ambos afectan directamente la seguridad de APIs. En exámenes de la Cámara de Industria y Comercio, en estudios de informática y en certificaciones se preguntan regularmente las diferencias entre autenticación y autorización, OAuth 2.0, JWT y headers de seguridad.
Conceptos fundamentales en detalle
Métodos de autenticación para REST APIs
API-Key
El cliente envía una clave estática en el header X-API-Key. El servidor la compara con su base de datos.
Ventajas: simple, adecuado para comunicación servidor a servidor. Desventajas: sin expiración, sin permisos granulares, acceso completo en caso de compromiso.
Autenticación basada en sesiones
El cliente envía credenciales (usuario/contraseña). El servidor crea una sesión, la almacena en su lado y devuelve una ID de sesión en una cookie. En cada nueva petición, el navegador envía la cookie automáticamente.
Ventajas: control en el servidor, la sesión puede invalidarse en cualquier momento. Desventajas: escalabilidad limitada con múltiples instancias (requiere session store compartido), riesgo de CSRF con cookies.
Autenticación basada en tokens (JWT)
El cliente envía credenciales. El servidor crea un JSON Web Token (JWT) que contiene claims como ID de usuario, roles y tiempo de expiración. El token está firmado criptográficamente. El cliente lo envía en cada petición en el header Authorization: Bearer <token>.
Ventajas: sin estado (stateless), escalable, no requiere session store. Desventajas: el token no puede revocarse fácilmente antes de expirar (excepto con listas de revocación o blacklists).
OAuth 2.0
OAuth 2.0 es un framework para autorización delegada. Un cliente recibe un access token de un servidor de autorización después de que el resource owner (el usuario) da su consentimiento. El token permite acceder a recursos específicos durante un tiempo limitado.
Flujos principales:
- Authorization Code Flow: estándar para aplicaciones web con backend servidor
- PKCE Flow: para SPAs y aplicaciones móviles sin client secret
- Client Credentials Flow: para comunicación servidor a servidor sin participación del usuario
Modelos de autorización
RBAC (Role-Based Access Control)
Los usuarios se asignan a roles. Los roles tienen permisos. La verificación es: ¿Tiene el usuario el rol que permite esta acción?
Ejemplo: el rol admin puede todo, el rol editor puede leer y escribir, el rol viewer solo puede leer.
Ventajas: fácil de entender, adecuado para sistemas con alcance manejable. Desventajas: explosión de roles con permisos muy granulares.
ABAC (Attribute-Based Access Control)
El control de acceso se basa en atributos del sujeto, recurso, acción y entorno. La verificación es: ¿Cumple la combinación de todos los atributos la política?
Ejemplo: un médico puede leer datos de pacientes solo durante sus horas de trabajo y solo para pacientes de su departamento.
Ventajas: muy granular, basado en políticas. Desventajas: más complejo de implementar y mantener.
Autorización basada en propiedad de recurso
El usuario solo puede acceder a recursos que le pertenecen. Una orden solo puede verla su propietario. La verificación requiere una consulta a la base de datos: ¿Pertenece este recurso al usuario autenticado?
Medidas de seguridad adicionales
TLS (Transport Layer Security)
Toda API debe ejecutarse sobre HTTPS. Sin TLS, los tokens y credenciales pueden ser interceptados. TLS cifra el transporte y garantiza la integridad de los datos transmitidos.
CORS (Cross-Origin Resource Sharing)
CORS determina qué orígenes externos pueden acceder a la API.
El servidor envía el header Access-Control-Allow-Origin.
Sin configuración CORS correcta, los clientes basados en navegador pueden bloquearse, o peor aún, orígenes arbitrarios obtienen acceso.
Protección CSRF
Con autenticación basada en cookies existe riesgo de CSRF. Un atacante puede lograr que el navegador envíe una cookie automáticamente.
Medidas de protección: tokens CSRF, atributo de cookie SameSite, header Authorization en lugar de cookies.
Rate Limiting
Limita el número de peticiones por cliente en un período de tiempo. Protege contra ataques de fuerza bruta, credential stuffing y DoS. Típicamente: 100 peticiones por minuto por token.
Validación de entrada
Toda entrada debe validarse. SQL injection, XSS y command injection surgen de validación faltante o insuficiente. Utiliza prepared statements, validación de esquemas y whitelisting.
¿Por qué es importante la seguridad en REST APIs en la práctica?
Una API insegura es una ventana abierta a tus datos. Escenarios realistas:
- Token comprometido: un access token se almacena en logs o se codifica en el código cliente. Un atacante lo encuentra y accede a todos los datos del usuario afectado.
- Broken Object Level Authorization (BOLA): la API solo verifica si el usuario está autenticado, pero no si tiene acceso al recurso solicitado. Un usuario llama a
/api/orders/42y ve la orden de otro usuario. - Mass Assignment: el cliente envía un campo como
{"role":"admin"}al actualizar el perfil. El servidor lo adopta sin verificación. El usuario se hace administrador a sí mismo. - Sin rate limiting: un atacante prueba 10.000 contraseñas por segundo. Sin rate limiting, rompe la contraseña en minutos.
Ejemplo práctico: API de Express.js con JWT y RBAC
Este ejemplo muestra una API REST completa con autenticación (JWT) y autorización (RBAC). Se eligió porque reúne los conceptos más importantes en código compacto: creación de tokens, verificación de autenticación basada en middleware, control de acceso basado en roles y verificación de propiedad.
// auth-middleware.js
// Middleware: verifica JWT del header Authorization
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
error: 'UNAUTHORIZED',
message: 'Se requiere autenticación. Envía un token Bearer.'
});
}
const token = authHeader.split(' ')[1];
try {
// jwt.verify lanza una excepción con token inválido o expirado
const payload = jwt.verify(token, process.env.JWT_SECRET);
req.user = payload; // { userId: 42, role: 'editor', exp: 1234567890 }
next();
} catch (err) {
return res.status(401).json({
error: 'INVALID_TOKEN',
message: 'El token es inválido o ha expirado.'
});
}
}
// Middleware: verifica si el usuario tiene uno de los roles requeridos
function authorize(...roles) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
error: 'UNAUTHORIZED',
message: 'Se requiere autenticación.'
});
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({
error: 'FORBIDDEN',
message: `Rol requerido: ${roles.join(' o ')}.`
});
}
next();
};
}
// Middleware: verifica si el usuario es propietario del recurso
function checkOwnership(getResourceId) {
return async (req, res, next) => {
const resourceId = getResourceId(req);
const resource = await db.orders.findById(resourceId);
if (!resource) {
return res.status(404).json({
error: 'NOT_FOUND',
message: 'Recurso no encontrado.'
});
}
// Los administradores pueden todo, otros solo sus propios recursos
if (req.user.role !== 'admin' && resource.userId !== req.user.userId) {
return res.status(403).json({
error: 'FORBIDDEN',
message: 'Solo puedes acceder a tus propios recursos.'
});
}
req.resource = resource;
next();
};
}
// routes.js
// Aplicación del middleware a varios endpoints
// Login: autenticación -> emisión de JWT
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
const user = await db.users.findByEmail(email);
if (!user || !await bcrypt.compare(password, user.passwordHash)) {
return res.status(401).json({
error: 'INVALID_CREDENTIALS',
message: 'Email o contraseña incorrectos.'
});
}
const token = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.json({ token });
});
// Rutas protegidas: authenticate para todos, authorize para roles específicos
app.get('/api/orders', authenticate, async (req, res) => {
// Usuarios normales ven solo sus órdenes
if (req.user.role === 'admin') {
const orders = await db.orders.findAll();
res.json(orders);
} else {
const orders = await db.orders.findByUserId(req.user.userId);
res.json(orders);
}
});
app.get('/api/orders/:id', authenticate, checkOwnership(req => req.params.id), (req, res) => {
res.json(req.resource);
});
app.post('/api/orders', authenticate, async (req, res) => {
const order = await db.orders.create({
...req.body,
userId: req.user.userId
});
res.status(201).json(order);
});
app.delete('/api/orders/:id', authenticate, authorize('admin'), checkOwnership(req => req.params.id), async (req, res) => {
await db.orders.delete(req.params.id);
res.status(204).send();
});
Información detallada
Tiempo de vida de tokens y Refresh Tokens
Los Access Tokens deben ser efímeros (15 minutos). Los Refresh Tokens tienen una vida más larga (días a semanas) y sirven únicamente para obtener nuevos Access Tokens. Si se compromete un Access Token, solo será útil durante poco tiempo. Si se compromete un Refresh Token, puede revocarse en el servidor.
Estructura JWT
Un JWT consta de tres partes codificadas en Base64: Header, Payload y Signature. El Header contiene el algoritmo y el tipo. El Payload contiene Claims como sub (Subject), exp (Expiration), iat (Issued At), role, userId. La Signature se calcula con el Secret o Private Key.
Importante: el Payload solo está codificado en Base64, no cifrado. Nunca almacenes datos sensibles en el JWT. La seguridad proviene de la firma, que garantiza que el token no ha sido alterado.
Headers de seguridad para REST APIs
| Header | Propósito |
|---|---|
Strict-Transport-Security | Fuerza HTTPS |
X-Content-Type-Options: nosniff | Previene MIME-Sniffing |
X-Frame-Options: DENY | Previene Clickjacking |
Cache-Control: no-store | Evita cachear respuestas sensibles |
Access-Control-Allow-Origin | Configuración CORS |
Vulnerabilidades comunes (OWASP API Security Top 10)
- BOLA (Broken Object Level Authorization): Sin verificación de propiedad
- Broken Authentication: Creación débil de tokens, sin Rate Limits
- Excessive Data Exposure: La API devuelve más campos de los necesarios
- Lack of Resources & Rate Limiting: Sin límite en el número de solicitudes
- Broken Function Level Authorization: Sin verificación de rol por endpoint
- Mass Assignment: Aceptación sin filtros de campos de entrada
- Security Misconfiguration: Secrets por defecto, modo debug, CORS abierto
Resumen de mejores prácticas
- Usa HTTPS siempre y exclusivamente
- Access Tokens efímeros (15 min), Refresh Tokens revocables
- RBAC para derechos generales, ABAC para políticas granulares
- Verificación de propiedad en cada endpoint que afecte recursos específicos
- Rate Limiting en nivel IP y nivel token
- Validación de entrada con esquema y whitelist
- Ningún dato sensible en el Payload del JWT
- Establece headers de seguridad
- Mensajes de error sin detalles internos
- Auditorías de seguridad y pruebas de penetración regulares
FAQ: Seguridad REST API
1. ¿Cuál es la diferencia entre autenticación y autorización?
2. ¿Qué es RBAC?
3. ¿Qué es ABAC?
4. ¿Qué es Broken Object Level Authorization (BOLA)?
5. ¿Por qué deben ser efímeros los Access Tokens?
6. ¿Cuál es la diferencia entre JWT y Session-Cookies?
7. ¿Qué es Mass Assignment y cómo prevenirlo?
8. ¿Por qué es importante CORS para la seguridad de API?
9. ¿Qué es CSRF y cómo afecta a REST APIs?
10. ¿Los Payloads de JWT están cifrados?
11. ¿Qué es el flujo PKCE en OAuth 2.0?
12. ¿Cómo funciona Rate Limiting en APIs?
13. ¿Qué códigos HTTP son relevantes para errores de autenticación?
14. ¿Qué es el Principle of Least Privilege?
15. ¿Cómo se revoca un JWT antes de su expiración?
Continuando en la ruta de aprendizaje de API
El siguiente artículo en la ruta de aprendizaje de API cubre API Security Best Practices: APIs schützen, absichern und betreiben — las medidas de seguridad más importantes para operar APIs siguiendo OWASP y mejores prácticas.
Referencias y recursos adicionales
- https://owasp.org/API-Security/editions/2023/en/0x11-t10/
- https://datatracker.ietf.org/doc/html/rfc6749 (OAuth 2.0)
- https://datatracker.ietf.org/doc/html/rfc7519 (JWT)
- https://owasp.org/www-project-top-ten/
- https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html
Lecturas recomendadas para desarrollo de APIs
Keine Bücher für Kategorie "api-development" gefunden.


