Principiante
18 min lectura
Autenticación y Autorización
Dos conceptos fundamentales de seguridad que a menudo se confunden pero tienen roles distintos
¿Qué son Autenticación y Autorización?
Autenticación (AuthN)
¿Quién eres? - Verificar la identidad del usuario.
Ejemplos: Login con usuario/contraseña, 2FA, biometría, OAuth
Autorización (AuthZ)
¿Qué puedes hacer? - Verificar los permisos del usuario.
Ejemplos: Roles, permisos, ACLs, RBAC, políticas de acceso
Importante
La autenticación siempre viene ANTES que la autorización. Primero verificamos quién eres, luego verificamos qué puedes hacer. No tiene sentido verificar permisos sin saber quién los solicita.
Autenticación en Profundidad
Factores de Autenticación
1. Algo que sabes (Knowledge)
- • Contraseñas, PINs
- • Preguntas de seguridad
- • Patrones de desbloqueo
2. Algo que tienes (Possession)
- • Tokens de hardware (YubiKey)
- • Teléfono móvil (SMS, apps)
- • Tarjetas de acceso
3. Algo que eres (Inherence)
- • Huellas dactilares
- • Reconocimiento facial
- • Escaneo de iris/retina
Ejemplo: Flujo de Autenticación
// Frontend - Login Request
async function login(username, password) {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const { token } = await response.json();
// Guardar token (¡VULNERABLE si es localStorage!)
localStorage.setItem('authToken', token);
return token;
}
// Backend - Verificar Credenciales (Node.js/Express)
app.post('/api/auth/login', async (req, res) => {
const { username, password } = req.body;
// 1. Buscar usuario
const user = await db.users.findOne({ username });
if (!user) {
return res.status(401).json({ error: 'Credenciales inválidas' });
}
// 2. Verificar contraseña (usando bcrypt)
const isValid = await bcrypt.compare(password, user.passwordHash);
if (!isValid) {
return res.status(401).json({ error: 'Credenciales inválidas' });
}
// 3. Generar token JWT
const token = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '24h' }
);
res.json({ token, user: { id: user.id, username: user.username } });
});Autorización en Profundidad
Modelos de Control de Acceso
Role-Based Access Control (RBAC)
Los permisos se asignan a roles, y los usuarios son asignados a roles.
// Definir roles y permisos
const roles = {
admin: ['read', 'write', 'delete', 'manage_users'],
editor: ['read', 'write'],
viewer: ['read']
};
// Middleware de autorización
function authorize(requiredPermission) {
return (req, res, next) => {
const userRole = req.user.role; // del token JWT
const permissions = roles[userRole] || [];
if (!permissions.includes(requiredPermission)) {
return res.status(403).json({
error: 'No tienes permisos para esta acción'
});
}
next();
};
}
// Uso en rutas
app.delete('/api/posts/:id',
authenticate, // primero autenticación
authorize('delete'), // luego autorización
deletePost
);Attribute-Based Access Control (ABAC)
Los permisos se basan en atributos del usuario, recurso y contexto.
// Política ABAC
function canEditDocument(user, document, context) {
return (
// El usuario es el propietario
document.ownerId === user.id ||
// El usuario es admin
user.role === 'admin' ||
// El usuario está en el departamento y el doc no está bloqueado
(user.department === document.department &&
!document.locked &&
context.time < document.deadline)
);
}
// Uso
app.put('/api/documents/:id', authenticate, async (req, res) => {
const document = await db.documents.findById(req.params.id);
const context = { time: new Date(), ip: req.ip };
if (!canEditDocument(req.user, document, context)) {
return res.status(403).json({ error: 'Acceso denegado' });
}
// Proceder con la edición
});Vulnerabilidades Comunes
Broken Authentication
- • Contraseñas débiles o predeterminadas
- • Tokens de sesión expuestos en URLs
- • Falta de rate limiting en login
- • Session fixation attacks
- • Almacenamiento inseguro de credenciales
Broken Access Control
- • IDOR (Insecure Direct Object References)
- • Falta de validación de permisos en APIs
- • Privilege escalation (vertical/horizontal)
- • Path traversal sin restricciones
- • Missing function level access control
Mejores Prácticas
Autenticación Segura
- ✓ Implementar Multi-Factor Authentication (2FA/MFA)
- ✓ Usar bcrypt/argon2 para hashear contraseñas
- ✓ Implementar rate limiting y CAPTCHA
- ✓ Usar HTTPS para todo el tráfico
- ✓ Implementar account lockout tras intentos fallidos
- ✓ Usar tokens seguros (JWT con firma HMAC/RSA)
Autorización Segura
- ✓ Verificar permisos en CADA petición (server-side)
- ✓ Usar principio de mínimo privilegio
- ✓ Implementar separation of duties
- ✓ Denegar por defecto (whitelist approach)
- ✓ Registrar todos los eventos de autorización
- ✓ Revisar y auditar permisos regularmente
Ejemplo: Sistema Completo
// middleware/auth.js
const jwt = require('jsonwebtoken');
// Middleware de autenticación
function authenticate(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Token no proporcionado' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Token inválido' });
}
}
// Middleware de autorización
function requireRole(...allowedRoles) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'No autenticado' });
}
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).json({
error: 'No tienes permisos',
required: allowedRoles,
current: req.user.role
});
}
next();
};
}
// routes/posts.js
app.get('/api/posts', authenticate, getPosts);
app.post('/api/posts', authenticate, requireRole('editor', 'admin'), createPost);
app.delete('/api/posts/:id', authenticate, requireRole('admin'), deletePost);
// Resource-level authorization
app.put('/api/posts/:id', authenticate, async (req, res) => {
const post = await db.posts.findById(req.params.id);
// Solo el autor o admin puede editar
if (post.authorId !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({ error: 'No puedes editar este post' });
}
// Actualizar post
await db.posts.update(req.params.id, req.body);
res.json({ success: true });
});