Firma de Requests con HMAC
Asegura requests de APIs con firmas HMAC-SHA256 para garantizar integridad y autenticidad.
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Visión General
HMAC (Hash-based Message Authentication Code) es el estándar de la industria para firmar requests de APIs. Al combinar un secreto compartido con el payload del request y un hash criptográfico, tanto emisor como receptor pueden verificar la integridad y autenticidad del mensaje sin transmitir el secreto por la red.
Cuándo Usar
Usa este recurso cuando:
- Autenticas llamadas API de servicio a servicio
- Aseguras que payloads de webhooks no han sido alterados
- Implementas autenticación de API keys sin la complejidad de OAuth
- Verificas integridad de requests a través de redes no confiables
Solución
Firma HMAC-SHA256 (Node.js)
const crypto = require('crypto');
function signRequest(method, path, body, timestamp, secret) {
const payload = method.toUpperCase() + path + timestamp + JSON.stringify(body);
return crypto.createHmac('sha256', secret).update(payload).digest('hex');
}
function verifyRequest(method, path, body, timestamp, signature, secret) {
const expected = signRequest(method, path, body, timestamp, secret);
// Comparación en tiempo constante
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
}
Ejemplo Cliente-Servidor (Python)
import hmac
import hashlib
import time
def sign_request(method: str, path: str, body: bytes, secret: str) -> str:
timestamp = str(int(time.time()))
message = f"{method.upper()}{path}{timestamp}{body.decode()}"
signature = hmac.new(
secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
return signature, timestamp
# Cliente
signature, ts = sign_request("POST", "/api/orders", b'{"id":1}', "my-secret")
headers = {"X-Signature": signature, "X-Timestamp": ts}
# Servidor
def verify(signature: str, timestamp: str, method, path, body, secret):
# Rechazar requests antiguos (protección contra replay)
if abs(int(time.time()) - int(timestamp)) > 300:
return False
expected, _ = sign_request(method, path, body, secret)
return hmac.compare_digest(signature, expected)
Explicación
La seguridad de HMAC se basa en tres propiedades:
- Clave secreta: Nunca transmitida; compartida out-of-band durante el onboarding
- Cobertura del mensaje: La firma debe cubrir method, path, timestamp y body
- Protección contra replay: Ventanas de tiempo previenen que atacantes reutilicen requests antiguos
¿Por qué no SHA-256 plano? SHA-256 sin HMAC es vulnerable a ataques de extensión de longitud. HMAC usa dos pasadas anidadas de hashing que previenen esto.
Variantes
| Algoritmo | Hash | Fortaleza | Notas |
|---|---|---|---|
| HMAC-SHA256 | SHA-256 | 128-bit | Recomendado por defecto |
| HMAC-SHA384 | SHA-384 | 192-bit | Mayor margen de seguridad |
| HMAC-SHA512 | SHA-512 | 256-bit | Más lento; usar en contextos de alta seguridad |
| HMAC-Blake3 | Blake3 | 256-bit | Rápido; alternativa moderna |
Mejores Prácticas
- Incluye timestamp: Rechaza requests más antiguos que 5 minutos para prevenir ataques de replay
- Firma todo el request: Method + path + timestamp + body (headers ordenados si se incluyen)
- Usa comparación en tiempo constante: timingSafeEqual previene ataques de timing
- Rota secretos regularmente: Usa versionado de claves (v1, v2) en el header de firma
- Nunca loguees el secreto: Loguea firmas y claves, nunca el secreto raw
Errores Comunes
- Firmar solo el body: Un atacante puede replayar un body válido con un endpoint diferente
- Faltar protección contra replay: Sin timestamps, requests interceptados son válidos para siempre
- Usar MD5 o SHA-1: Criptográficamente rotos; usar mínimo SHA-256
- Comparación de strings en lugar de timingSafeEqual: Vulnerable a ataques de timing
- Almacenar secretos en variables de entorno sin encriptar: Usa un secret manager
Preguntas Frecuentes
P: ¿Es HMAC mejor que JWT para auth de servicio a servicio? R: HMAC es más simple y stateless para servicios internos. JWT es mejor cuando necesitas claims de identidad y verificación por terceros. Para una visión completa de seguridad de API, consulta el checklist de seguridad API.
P: ¿Cómo manejo desviación de reloj entre servicios? R: Permite una ventana de 5 minutos y sincroniza con NTP. Rechaza requests fuera de la ventana.
P: ¿Puedo usar el mismo secreto para múltiples clientes? R: No. Cada cliente debería tener un secreto único para poder revocar uno sin afectar a otros.
Recursos Relacionados
API Security Checklist — Authentication to Encryption
A comprehensive security checklist for APIs: authentication, authorization, input validation, rate limiting, encryption, logging, and deployment hardening.
GuideSecurity Best Practices Guide
A comprehensive guide to application security: authentication, authorization, input validation, secrets management, and common vulnerability prevention.
GuideWeb Application Security (OWASP Top 10)
A developer-focused guide to the OWASP Top 10: injection, broken access control, XSS, insecure design, and how to prevent each vulnerability with code examples.
RecipeWebSocket Authentication and Security Patterns
How to authenticate WebSocket connections, implement token validation, and handle authorization for real-time messaging in production
RecipeProtect Web Forms Against CSRF Attacks
How to prevent Cross-Site Request Forgery attacks using synchronizer tokens, SameSite cookies, and double-submit cookie patterns.