Guia Completa de JWT 2026 - Estructura, Seguridad y Mejores Practicas
Desde la estructura y funcionamiento de JSON Web Token hasta vulnerabilidades de seguridad y mejores practicas. Domina la autenticacion JWT con ejemplos de codigo practicos.
Toolypet Team
Development Team
Guia Completa de JWT 2026
"JWT? No es solo un token?"
Si, lo es. Pero cuando ese "simple token" no se implementa correctamente, se convierte en un problema de seguridad grave. Segun estudios de 2026, el 48% del codigo de autenticacion generado por IA contiene vulnerabilidades de seguridad.
Esta guia cubre todo, desde la estructura de JWT hasta las mejores practicas de seguridad.
Que es JWT?
JWT (JSON Web Token) es un metodo compacto y autocontenido para transmitir informacion de forma segura entre dos partes.
Caracteristicas Principales
| Caracteristica | Descripcion |
|---|---|
| Stateless | No requiere almacenamiento de sesion en el servidor |
| Self-contained | Toda la informacion necesaria esta en el token |
| Compact | Utilizable en URLs, headers HTTP |
| Signed | Se puede detectar manipulacion |
Casos de Uso
- Autenticacion API: Solicitudes API con Bearer token
- SSO (Single Sign-On): Compartir autenticacion entre multiples servicios
- Intercambio de informacion: Transmision de datos firmados
- Authorization: Incluir informacion de permisos/roles
Estructura de JWT
JWT consiste en tres partes codificadas en Base64URL separadas por . (puntos).
xxxxx.yyyyy.zzzzz
| | |
Header Payload Signature
1. Header
{
"alg": "HS256",
"typ": "JWT"
}
| Campo | Descripcion |
|---|---|
alg | Algoritmo de firma (HS256, RS256, etc.) |
typ | Tipo de token (JWT) |
2. Payload (Claims)
{
"sub": "user123",
"name": "Juan Garcia",
"email": "juan@example.com",
"role": "admin",
"iat": 1708502400,
"exp": 1708588800
}
Claims Registrados
| Claim | Descripcion | Ejemplo |
|---|---|---|
iss | Emisor (Issuer) | "https://auth.example.com" |
sub | Sujeto (Subject) | "user123" |
aud | Audiencia (Audience) | "https://api.example.com" |
exp | Tiempo de expiracion | 1708588800 |
nbf | No antes de (Not Before) | 1708502400 |
iat | Emitido en (Issued At) | 1708502400 |
jti | ID unico de JWT | "abc123" |
Claims Publicos/Privados
{
"role": "admin", // Claim privado
"email": "user@example.com",
"permissions": ["read", "write"]
}
3. Signature
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
Algoritmos de Firma
Clave Simetrica
| Algoritmo | Descripcion | Caso de Uso |
|---|---|---|
| HS256 | HMAC + SHA-256 | Servidor unico, implementacion simple |
| HS384 | HMAC + SHA-384 | Mayor seguridad |
| HS512 | HMAC + SHA-512 | Seguridad de nivel maximo |
// Node.js - HS256
const jwt = require('jsonwebtoken');
const token = jwt.sign(
{ userId: 123, role: 'admin' },
'your-256-bit-secret', // Verificado con el mismo secreto
{ algorithm: 'HS256', expiresIn: '1h' }
);
Clave Asimetrica
| Algoritmo | Descripcion | Caso de Uso |
|---|---|---|
| RS256 | RSA + SHA-256 | Microservicios, verificacion publica |
| RS384 | RSA + SHA-384 | Requisitos de alta seguridad |
| RS512 | RSA + SHA-512 | Seguridad de nivel maximo |
| ES256 | ECDSA + P-256 | Claves cortas, movil |
// RS256 - Asimetrico
const privateKey = fs.readFileSync('private.pem');
const publicKey = fs.readFileSync('public.pem');
// Emision (Private Key)
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });
// Verificacion (Public Key)
const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] });
Guia de Seleccion de Algoritmo
| Escenario | Algoritmo Recomendado |
|---|---|
| Servidor unico | HS256 |
| Microservicios | RS256 |
| Verificacion publica necesaria | RS256 / ES256 |
| Movil/IoT | ES256 (claves cortas) |
Implementacion Practica
Node.js (Express)
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const SECRET = process.env.JWT_SECRET; // Cargar desde variable de entorno
// Emision de token
app.post('/login', async (req, res) => {
const { email, password } = req.body;
// Verificacion de usuario (ejemplo)
const user = await verifyCredentials(email, password);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign(
{
sub: user.id,
email: user.email,
role: user.role,
},
SECRET,
{
expiresIn: '1h',
issuer: 'your-app-name',
}
);
res.json({ token });
});
// Verificacion con middleware
const authMiddleware = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Missing token' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, SECRET, {
issuer: 'your-app-name',
algorithms: ['HS256'],
});
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
};
// Ruta protegida
app.get('/protected', authMiddleware, (req, res) => {
res.json({ message: `Hello, ${req.user.email}` });
});
Python (FastAPI)
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
from datetime import datetime, timedelta
import os
app = FastAPI()
security = HTTPBearer()
SECRET = os.environ.get("JWT_SECRET")
# Emision de token
def create_token(user_id: str, role: str) -> str:
payload = {
"sub": user_id,
"role": role,
"iat": datetime.utcnow(),
"exp": datetime.utcnow() + timedelta(hours=1),
}
return jwt.encode(payload, SECRET, algorithm="HS256")
# Verificacion de token
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
token = credentials.credentials
try:
payload = jwt.decode(token, SECRET, algorithms=["HS256"])
return payload
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token")
# Endpoint protegido
@app.get("/protected")
def protected_route(user: dict = Depends(verify_token)):
return {"message": f"Hello, {user['sub']}"}
Vulnerabilidades de Seguridad y Contramedidas
1. Ataque Algorithm None
// Header malicioso
{
"alg": "none",
"typ": "JWT"
}
Contramedida:
// Especificar algoritmos permitidos
jwt.verify(token, secret, { algorithms: ['HS256'] }); // OK
jwt.verify(token, secret); // Peligroso
2. Clave Secreta Debil
// Peligroso
const SECRET = 'secret123';
// Seguro (minimo 256 bits)
const SECRET = crypto.randomBytes(32).toString('hex');
3. Exposicion de Informacion Sensible
// Peligroso: Payload puede ser decodificado en Base64
{
"password": "user_password", // Nunca hacer esto!
"creditCard": "1234-5678-9012-3456"
}
// Seguro
{
"sub": "user123",
"role": "admin"
}
4. Robo de Token
Estrategias de contramedida:
- Tiempo de expiracion corto (15 min ~ 1 hora)
- Usar Refresh Token
- HTTPS obligatorio
- Cookies HttpOnly (prevencion XSS)
5. Ataque de Replay JWT
// Usar jti (JWT ID)
const token = jwt.sign({
sub: user.id,
jti: crypto.randomUUID(), // ID unico
}, SECRET);
// Gestionar lista negra de jti en el servidor
Access Token + Refresh Token
Por que es necesario?
| Token | Duracion | Proposito |
|---|---|---|
| Access Token | 15 min ~ 1 hora | Autenticacion API |
| Refresh Token | 7 ~ 30 dias | Renovar Access Token |
Ejemplo de Implementacion
// Emitir ambos tokens en login
app.post('/login', async (req, res) => {
const user = await verifyCredentials(req.body);
const accessToken = jwt.sign(
{ sub: user.id, type: 'access' },
ACCESS_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ sub: user.id, type: 'refresh' },
REFRESH_SECRET,
{ expiresIn: '7d' }
);
// Refresh Token como cookie HttpOnly
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
res.json({ accessToken });
});
// Renovacion de token
app.post('/refresh', (req, res) => {
const refreshToken = req.cookies.refreshToken;
try {
const decoded = jwt.verify(refreshToken, REFRESH_SECRET);
const newAccessToken = jwt.sign(
{ sub: decoded.sub, type: 'access' },
ACCESS_SECRET,
{ expiresIn: '15m' }
);
res.json({ accessToken: newAccessToken });
} catch (err) {
res.status(401).json({ error: 'Invalid refresh token' });
}
});
Depuracion de JWT
Decodificacion de Token
Pega tu token en JWT Decoder para:
- Verificar Header
- Verificar Payload
- Verificar tiempo de expiracion
- Verificar firma (cuando se proporciona secreto)
Decodificacion CLI
# Decodificar Header
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d
# Decodificar Payload
echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ" | base64 -d
Lista de Verificacion de Mejores Practicas
Emision
- Clave secreta fuerte (256 bits o mas)
- Especificar algoritmo (HS256, RS256)
- Tiempo de expiracion corto (15 min ~ 1 hora)
- Incluir claims requeridos (sub, iat, exp, iss)
- Sin informacion sensible
Verificacion
- Lista blanca de algoritmos (
algorithms: ['HS256']) - Validacion de tiempo de expiracion
- Validacion de emisor (iss)
- Validacion de audiencia (aud)
Almacenamiento y Transmision
- HTTPS obligatorio
- Cookie HttpOnly (prevencion XSS)
- Atributo SameSite (prevencion CSRF)
- Evitar localStorage (vulnerable a XSS)
Preguntas Frecuentes
Q1: JWT vs Session, cuando usar cual?
R:
- JWT: API stateless, microservicios, apps moviles
- Session: Apps web tradicionales, renderizado del lado del servidor, logout inmediato requerido
Q2: Que pasa si JWT es robado?
R: Access Token permanece valido hasta expirar. Por lo tanto:
- Establecer tiempo de expiracion corto (15 min)
- Refresh Token Rotation
- Invalidar todos los tokens cuando hay sospecha
Q3: Por que no deberia almacenar en localStorage?
R: Es vulnerable a ataques XSS. Como JavaScript puede acceder, scripts maliciosos pueden robar tokens. Las cookies HttpOnly son mas seguras.
Q4: Es RS256 mas seguro que HS256?
R: No es "mas seguro" sino "proposito diferente".
- HS256: Mismo secreto compartido, servidor unico
- RS256: Verificar con clave publica, multiples servicios
Q5: Necesito una lista negra de tokens?
R: Si necesitas logout inmediato, si. Pero reduce la ventaja de stateless. Expiracion corta + Refresh Token es una alternativa.
Conclusion
Puntos clave de seguridad JWT:
- Secreto fuerte: 256 bits o mas aleatorios
- Verificacion de algoritmo: Enfoque de lista blanca
- Duracion corta: Access 15 min, Refresh 7 dias
- Almacenamiento seguro: Cookies HttpOnly + Secure
- Sin info sensible: Payload es decodificable por cualquiera
Herramientas Relacionadas
| Herramienta | Proposito |
|---|---|
| JWT Decoder | Decodificacion y verificacion JWT |
| Base64 Encoder | Codificacion/decodificacion Base64 |
| Hash Generator | Generacion de hash |
Sobre el Autor
Toolypet Team
Development Team
The Toolypet Team creates free, privacy-focused web tools for developers and designers. All tools run entirely in your browser with no data sent to servers.