De primitivas a arquitectura: decisiones que sí cambian el resultado
En aplicaciones web, móviles y APIs, la criptografía rara vez falla por “el algoritmo”: falla por decisiones de diseño (dónde se guarda un token, qué se firma, qué se cifra, qué se valida y en qué orden). Este capítulo traduce necesidades típicas (sesión, antifraude, integridad, sincronización) a mecanismos concretos sin reinventar protocolos.
Mapa rápido: problema → mecanismo recomendado
| Necesidad | Mecanismo | Errores comunes |
|---|---|---|
| Sesión web | Cookie de sesión + flags seguros | JWT en localStorage; cookies sin SameSite; no rotar sesión |
| API para SPA/móvil | OAuth 2.0/OIDC (conceptual) + tokens firmados | Inventar “tokens” propios; mezclar authn/authz; scopes mal definidos |
| Evitar CSRF | SameSite + token CSRF (si aplica) | Confiar solo en Referer; CSRF token sin atarlo a sesión |
| Integridad de eventos entrantes (webhooks) | Firma HMAC o firma asimétrica + timestamp | Firmar solo parte del payload; no validar ventana temporal; comparar firmas inseguro |
| Confidencialidad de payloads a nivel aplicación | Cifrado de payload (JWE/JOSE o esquema propio con AEAD) cuando TLS no basta | Cifrar “por si acaso”; no definir AAD; no versionar formato |
| Integridad extremo a extremo | Firmas de mensajes a nivel dominio (cliente firma, servidor verifica) | Confiar en TLS como E2E; no incluir contexto (audiencia, nonce, versión) |
Tokens en la práctica: JWT vs PASETO y cuándo usarlos
Decisión 1: ¿token auto-contenido o sesión opaca?
- Sesión opaca (cookie de sesión o bearer opaco): el servidor guarda estado (o lo delega a un store). Ventaja: revocación inmediata, tamaño pequeño, menos riesgo de exposición de claims. Ideal para web tradicional.
- Token auto-contenido (JWT/PASETO): el token lleva claims firmados (y a veces cifrados). Ventaja: validación local en microservicios, menos dependencia de store. Ideal para APIs distribuidas cuando necesitas validación sin llamada central.
Regla práctica: si necesitas revocación fuerte y simple, prefiere sesión opaca. Si necesitas validación distribuida y aceptas revocación por expiración/rotación, considera tokens auto-contenidos.
Decisión 2: JWT o PASETO
- JWT: estándar ubicuo. Útil cuando necesitas interoperabilidad amplia (gateways, proveedores, librerías corporativas). Requiere disciplina: fijar algoritmo, validar claims, evitar configuraciones “flexibles”.
- PASETO: diseñado para reducir ambigüedades. Menos “footguns” (por ejemplo, evita el campo
algnegociable). Útil cuando controlas emisor y consumidores y quieres una opción más opinada.
Decisión práctica: si tu ecosistema ya está montado sobre JWT (OIDC, API gateways), usa JWT pero con un perfil estricto. Si estás diseñando desde cero y controlas clientes/servicios, PASETO puede simplificar.
Guía paso a paso: perfil mínimo seguro para tokens firmados (JWT/PASETO)
- Define el propósito del token: acceso a API (autorización) vs identidad (autenticación). No mezcles: un token de acceso no debería ser “prueba de login” para UI.
- Fija audiencia y emisor: define valores exactos para
issyaud(o equivalentes) y valida ambos. - Expiración corta: define
expcorto (p.ej. 5–15 min) y usa refresh tokens o reautenticación según riesgo. - Incluye identificador único:
jtipara trazabilidad y, si necesitas revocación selectiva, para listas de revocación. - Valida el “tiempo” con tolerancia:
nbf/iatcon un pequeño clock skew. - Versiona claims: añade
verotoken_versionpara migraciones. - Autoriza por scopes/roles explícitos: evita inferir permisos por email/dominio. Documenta scopes y su significado.
- Rotación de claves: usa
kid(o equivalente) y un endpoint JWKS si aplica; define ventana de convivencia de claves.
Ejemplo de claims típicos para un token de acceso:
{ "iss": "https://auth.ejemplo.com", "aud": "api://payments", "sub": "user_123", "scope": "payments:read payments:write", "exp": 1735689600, "iat": 1735689000, "jti": "b7f1...", "ver": 2}Antipatrón: JWT en localStorage para web
En navegadores, guardar tokens bearer en localStorage o sessionStorage aumenta el impacto de XSS (exfiltración directa). Para web, suele ser preferible cookie HttpOnly (no accesible desde JS) y controles CSRF adecuados.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
Cookies seguras y sesiones web
Checklist de cookie de sesión
- HttpOnly: evita lectura por JavaScript.
- Secure: solo por HTTPS.
- SameSite:
Laxpor defecto;Strictsi tu UX lo permite;Nonesolo si necesitas third-party y siempre conSecure. - Path/Domain mínimos: restringe alcance.
- Rotación de sesión: al login, elevación de privilegios o cambio de credenciales.
Diseño recomendado: cookie de sesión opaca (ID aleatorio) + estado en servidor (o store). Si necesitas “stateless”, evalúa cuidadosamente el impacto de revocación y exposición de claims.
CSRF: cuándo aplica y cómo mitigarlo sin confusión
CSRF es relevante cuando el navegador envía credenciales automáticamente (cookies). Si usas cookies para autenticar, necesitas mitigación CSRF. Si usas Authorization: Bearer y el token no se envía automáticamente, CSRF suele ser menos relevante (pero XSS sigue siendo crítico).
Mitigación práctica por capas:
- SameSite=Lax reduce muchos CSRF “clásicos”.
- Token CSRF para acciones sensibles o cuando SameSite no cubre tu caso (p.ej. flujos cross-site necesarios).
- Validación de Origin como control adicional (no único).
Guía paso a paso: patrón CSRF con doble submit (cuando no puedes usar estado servidor)
- Servidor emite cookie
csrf_token(no HttpOnly) con valor aleatorio. - Frontend lee
csrf_tokeny lo envía en headerX-CSRF-Tokenen requests mutantes (POST/PUT/PATCH/DELETE). - Servidor valida que cookie y header coinciden y que la sesión es válida.
- Opcional: ata el token a sesión mediante HMAC o incluye un identificador de sesión en el token para evitar reutilización entre sesiones.
Nota de diseño: si ya tienes estado de sesión en servidor, un token CSRF almacenado en servidor (sin depender de doble submit) suele ser más simple de razonar.
Cifrado de payloads a nivel aplicación: cuándo TLS no basta
TLS protege en tránsito entre dos puntos. A veces necesitas protección adicional: intermediarios (proxies, gateways), almacenamiento en colas/logs, o requisitos de “solo el destinatario final puede leer” (confidencialidad end-to-end a nivel aplicación).
Casos típicos donde sí tiene sentido
- Mensajes que pasan por sistemas intermedios no confiables (colas, buses, integraciones B2B).
- Datos altamente sensibles que podrían terminar en logs/observabilidad.
- Arquitecturas donde un gateway termina TLS pero no debe ver el contenido.
Decisiones de diseño para cifrado de payload
- Define el formato: versiona el sobre (envelope) y especifica campos obligatorios.
- Incluye AAD: ata el cifrado a contexto (ruta, método, id de mensaje, audiencia) para evitar replays o confusión de destino.
- Gestión de claves por destinatario: una clave por servicio/tenant/usuario según el modelo; documenta rotación y compatibilidad.
- Errores uniformes: no filtrar detalles de validación/descifrado al atacante.
Ejemplo de “sobre” (envelope) versionado para un payload cifrado:
{ "v": 1, "kid": "svc-payments-2026-01", "alg": "AEAD", "nonce": "...", "aad": {"aud":"payments","msg_id":"..."}, "ciphertext": "..."}Firmas de webhooks: integridad, autenticidad y anti-replay
Un webhook es una petición entrante desde un tercero. Tu objetivo es verificar que el emisor legítimo lo envió y que el contenido no fue modificado. TLS ayuda, pero no sustituye una firma a nivel mensaje (especialmente si hay reintentos, proxies o múltiples rutas).
Patrón recomendado (HMAC) para webhooks
- Proveedor y consumidor comparten un secreto por endpoint/tenant.
- El proveedor firma una representación canónica del request (timestamp + método + path + body).
- El consumidor recalcula y compara en tiempo constante.
- Se rechazan timestamps fuera de ventana y se deduplican IDs para evitar replay.
Guía paso a paso: verificación de webhook con HMAC
- Obtén el raw body exactamente como llegó (antes de parsear JSON). Evita reserializar.
- Extrae headers:
X-Signature,X-Timestamp,X-Event-Id. - Valida ventana temporal: por ejemplo ±5 minutos.
- Construye el string a firmar (canónico):
ts + "." + method + "." + path + "." + raw_body. - Recalcula HMAC con el secreto correcto (por tenant o endpoint).
- Compara en tiempo constante y rechaza si no coincide.
- Anti-replay: guarda
event_idpor un TTL y rechaza duplicados.
Ejemplo de pseudocódigo:
signed = ts + "." + method + "." + path + "." + rawBodyexpected = HMAC_SHA256(secret, signed)if !constantTimeEquals(expected, providedSig): rejectif abs(now - ts) > 300s: rejectif isReplayed(eventId): rejectprocess()¿HMAC o firma asimétrica?
- HMAC: simple y eficiente; requiere compartir secreto (gestión por tenant).
- Asimétrica: el proveedor firma con privada, tú verificas con pública; útil cuando no quieres secretos compartidos o hay muchos consumidores. Requiere distribución/rotación de claves públicas (p.ej. JWKS).
Integridad extremo a extremo (E2E) a nivel de dominio
“E2E” aquí significa que el cliente final (web/móvil) produce una prueba criptográfica sobre datos de negocio, y el servidor verifica que esos datos no fueron alterados entre la captura y el procesamiento, incluso si hay componentes intermedios.
Ejemplos donde aporta valor
- Órdenes de pago: el cliente firma importe, moneda, destinatario y un nonce.
- Consentimientos: el cliente firma el texto/versión del consentimiento y la marca temporal.
- Telemetría sensible: el dispositivo firma mediciones para detectar manipulación.
Decisiones clave
- Qué se firma: incluye campos críticos y contexto (audiencia, versión del esquema, nonce, timestamp).
- Prevención de replay: nonce o contador por sesión/dispositivo.
- Identidad de clave: cómo asocias la clave pública al usuario/dispositivo (registro, attestation opcional, rotación).
Ejemplo de estructura firmada (canónica) para una orden:
{ "schema": "payment_order/v3", "aud": "api://payments", "user_id": "user_123", "device_id": "dev_456", "nonce": "n-789", "ts": 1735689000, "amount": "125.00", "currency": "EUR", "beneficiary": "acct_..."}Consideraciones móviles: almacenamiento seguro y datos locales
Keystore/Keychain: qué guardar y qué no
- Guardar: claves privadas del dispositivo, secretos de largo plazo (si el modelo lo requiere), material para desbloquear almacenamiento cifrado, tokens de refresh (según riesgo y políticas).
- No guardar: tokens de acceso de larga duración sin protección adicional; secretos compartidos globales embebidos en la app; claves “maestras” iguales para todos los usuarios.
Recomendación práctica: usa el almacén seguro del SO (Android Keystore / iOS Keychain) para generar y custodiar claves no exportables cuando sea posible. Para datos locales, cifra con una clave derivada/protegida por el almacén seguro y aplica controles de bloqueo (biometría/PIN) según sensibilidad.
Protección de datos locales: patrón de “sobre” para almacenamiento
Para bases locales (SQLite), archivos o cachés:
- Clasifica datos: PII, tokens, datos de negocio, caché no sensible.
- Define política de persistencia: qué se puede cachear, TTL, borrado al logout.
- Cifra en reposo lo sensible: usa un DEK (data encryption key) por usuario o por instalación.
- Protege el DEK: envuélvelo (wrap) con una clave del Keystore/Keychain o usa claves no exportables para operar.
- Integridad: usa formatos que detecten manipulación (AEAD) y valida antes de usar.
Sincronización segura móvil ↔ backend
Problemas típicos: conflictos, reintentos, offline, y riesgo de replay. Diseño recomendado:
- Identificadores idempotentes por operación (
op_id) para reintentos seguros. - Versionado de registros (ETag/versión) para detectar conflictos.
- Firmas por operación (opcional) si necesitas integridad E2E: el cliente firma el “delta” con nonce y versión.
- Minimiza datos en tránsito: envía solo cambios, no dumps completos.
APIs: autenticación y autorización sin reinventar criptografía
Separar conceptos: OAuth/OIDC vs mecanismos criptográficos
- OAuth 2.0: marco de autorización delegada (quién puede hacer qué). Define roles (resource owner, client, authorization server, resource server) y flujos (authorization code, client credentials, etc.).
- OpenID Connect (OIDC): capa de identidad sobre OAuth (quién es el usuario), con
id_tokeny endpoints estándar. - Mecanismos criptográficos: firmas de tokens, validación de claims, intercambio de claves, rotación, etc. Son “cómo se implementa” la seguridad del marco.
Regla práctica: adopta OAuth/OIDC para el modelo y flujos; usa tokens firmados y validación estricta para la parte criptográfica. No inventes un “mini-OAuth” casero.
Flujos de integración típicos (con decisiones de diseño)
1) SPA + API (usuario final)
- Recomendación: Authorization Code + PKCE.
- Token de acceso: corto, para API.
- Refresh token: si aplica, guárdalo con máxima protección (en web, preferir rotación y almacenamiento seguro vía cookie HttpOnly en backend-for-frontend).
- Validaciones en API:
iss,aud,exp, scopes, ykidcontra JWKS.
2) App móvil + API
- Recomendación: Authorization Code + PKCE con navegador del sistema (no webview embebida).
- Almacenamiento: refresh token en Keychain/Keystore; access token en memoria o almacenamiento efímero.
- Defensa adicional: atar sesión a dispositivo (device_id), detección de jailbreak/root según política, y rotación de refresh tokens.
3) Servicio a servicio (microservicios)
- Recomendación: Client Credentials (OAuth) o mTLS + autorización por claims.
- Tokens: audiencias específicas por servicio; scopes mínimos.
- Rotación: claves y credenciales con caducidad y automatización.
Cómo documentar requisitos criptográficos para equipos (plantilla utilizable)
La documentación efectiva reduce errores de implementación. Debe ser verificable (tests), versionada y con ejemplos. Puedes alojarla en plataformas de aprendizaje internas (p.ej., módulos en LMS corporativo) y en el repositorio (README/ADR).
Plantilla de especificación (para un endpoint o integración)
| Sección | Qué incluir |
|---|---|
| Objetivo | Qué protege: sesión, integridad de evento, confidencialidad de payload, E2E, etc. |
| Actores | Cliente, API, auth server, terceros (webhooks), intermediarios. |
| Credenciales | Tipo: cookie de sesión, access token, refresh token, secreto HMAC por tenant, clave pública, etc. |
| Formato | Headers, claims, campos firmados/cifrados, canonicalización, versión del sobre. |
| Validaciones | Lista exacta: iss/aud/exp/scope; timestamp window; anti-replay; constant-time compare. |
| Rotación | Cómo cambia kid, ventanas de convivencia, manejo de claves antiguas. |
| Errores | Códigos y mensajes (no filtrar detalles), métricas y logging seguro. |
| Ejemplos | Requests/responses completos, casos válidos e inválidos. |
| Tests | Casos de prueba: token expirado, aud incorrecta, firma inválida, replay, clock skew. |
Ejemplo de requisitos para un webhook (extracto listo para pegar)
- Headers obligatorios:
X-Signature(hex/base64),X-Timestamp(unix seconds),X-Event-Id(UUID). - String a firmar:
{ts}.{method}.{path}.{raw_body}(UTF-8). - Algoritmo: HMAC-SHA-256.
- Ventana temporal: 300s.
- Anti-replay: rechazar
X-Event-Idrepetidos durante 24h. - Comparación: tiempo constante.
- Respuesta ante fallo: HTTP 401 sin detalles; log interno con correlation id.