Toolypet
Volver al Blog
Dev

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

Toolypet Team

Development Team

9 min de lectura

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

CaracteristicaDescripcion
StatelessNo requiere almacenamiento de sesion en el servidor
Self-containedToda la informacion necesaria esta en el token
CompactUtilizable en URLs, headers HTTP
SignedSe 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"
}
CampoDescripcion
algAlgoritmo de firma (HS256, RS256, etc.)
typTipo de token (JWT)

2. Payload (Claims)

{
  "sub": "user123",
  "name": "Juan Garcia",
  "email": "juan@example.com",
  "role": "admin",
  "iat": 1708502400,
  "exp": 1708588800
}

Claims Registrados

ClaimDescripcionEjemplo
issEmisor (Issuer)"https://auth.example.com"
subSujeto (Subject)"user123"
audAudiencia (Audience)"https://api.example.com"
expTiempo de expiracion1708588800
nbfNo antes de (Not Before)1708502400
iatEmitido en (Issued At)1708502400
jtiID 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

AlgoritmoDescripcionCaso de Uso
HS256HMAC + SHA-256Servidor unico, implementacion simple
HS384HMAC + SHA-384Mayor seguridad
HS512HMAC + SHA-512Seguridad 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

AlgoritmoDescripcionCaso de Uso
RS256RSA + SHA-256Microservicios, verificacion publica
RS384RSA + SHA-384Requisitos de alta seguridad
RS512RSA + SHA-512Seguridad de nivel maximo
ES256ECDSA + P-256Claves 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

EscenarioAlgoritmo Recomendado
Servidor unicoHS256
MicroserviciosRS256
Verificacion publica necesariaRS256 / ES256
Movil/IoTES256 (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?

TokenDuracionProposito
Access Token15 min ~ 1 horaAutenticacion API
Refresh Token7 ~ 30 diasRenovar 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:

  1. Secreto fuerte: 256 bits o mas aleatorios
  2. Verificacion de algoritmo: Enfoque de lista blanca
  3. Duracion corta: Access 15 min, Refresh 7 dias
  4. Almacenamiento seguro: Cookies HttpOnly + Secure
  5. Sin info sensible: Payload es decodificable por cualquiera

Herramientas Relacionadas

HerramientaProposito
JWT DecoderDecodificacion y verificacion JWT
Base64 EncoderCodificacion/decodificacion Base64
Hash GeneratorGeneracion de hash
JWTAutenticacionSeguridadAPIDesarrollo WebToken

Sobre el Autor

Toolypet Team

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.

Web DevelopmentCSS ToolsDeveloper ToolsSEOSecurity