JWT e Boas Práticas JSON
Aula 4 de 4
JWT — JSON Web Token
JWT é um padrão aberto (RFC 7519) para transmissão segura de informações entre partes como um objeto JSON.
Estrutura do JWT
Um JWT é composto por três partes codificadas em Base64URL separadas por pontos:
HEADER.PAYLOAD.SIGNATURE
// HEADER
{
"alg": "HS256",
"typ": "JWT"
}
// PAYLOAD (claims registradas)
{
"sub": "1234567890",
"name": "João Silva",
"iat": 1516239022,
"exp": 1516242622,
"iss": "https://auth.exemplo.com",
"aud": ["https://api.exemplo.com"]
}
// SIGNATURE (HMAC-SHA256)
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret_key
)
Geração e Validação
Node.js com jsonwebtoken
const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET || 'minha-chave-secreta';
// Gerar token
const token = jwt.sign(
{
sub: 'user-123',
name: 'Maria Costa',
role: 'admin'
},
SECRET,
{
expiresIn: '1h',
issuer: 'https://auth.exemplo.com',
audience: 'https://api.exemplo.com'
}
);
console.log('Token:', token);
// Validar e decodificar
try {
const decoded = jwt.verify(token, SECRET, {
issuer: 'https://auth.exemplo.com',
audience: 'https://api.exemplo.com'
});
console.log('Válido!', decoded);
} catch (err) {
console.error('Inválido:', err.message);
}
Python com PyJWT
import jwt
from datetime import datetime, timedelta
SECRET = "minha-chave-secreta"
# Gerar token
payload = {
"sub": "user-123",
"name": "Maria Costa",
"role": "admin",
"iat": datetime.utcnow(),
"exp": datetime.utcnow() + timedelta(hours=1),
"iss": "https://auth.exemplo.com",
"aud": "https://api.exemplo.com"
}
token = jwt.encode(payload, SECRET, algorithm="HS256")
print(f"Token: {token}")
# Validar
try:
decoded = jwt.decode(
token,
SECRET,
algorithms=["HS256"],
audience="https://api.exemplo.com",
issuer="https://auth.exemplo.com"
)
print(f"Válido! Usuário: {decoded['name']}, Role: {decoded['role']}")
except jwt.ExpiredSignatureError:
print("Token expirado")
except jwt.InvalidAudienceError:
print("Audience inválida")
except jwt.InvalidTokenError as e:
print(f"Token inválido: {e}")
Segurança: Vulnerabilidades Comuns
Ataque alg=none
# VULNERÁVEL: aceitar algoritmo none
token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiJ9."
# SEGURO: forçar lista de algoritmos
try:
decoded = jwt.decode(token, SECRET, algorithms=["HS256"])
except:
print("Token inválido: algoritmo não permitido")
Refresh Tokens
const jwt = require('jsonwebtoken');
const ACCESS_SECRET = process.env.ACCESS_SECRET || 'access-secret';
const REFRESH_SECRET = process.env.REFRESH_SECRET || 'refresh-secret';
function generateTokens(userId) {
// Access token: curta duração
const accessToken = jwt.sign(
{ sub: userId, type: 'access' },
ACCESS_SECRET,
{ expiresIn: '15m' }
);
// Refresh token: longa duração
const refreshToken = jwt.sign(
{ sub: userId, type: 'refresh' },
REFRESH_SECRET,
{ expiresIn: '7d' }
);
return { accessToken, refreshToken };
}
function refreshAccessToken(refreshToken) {
try {
const decoded = jwt.verify(refreshToken, REFRESH_SECRET);
if (decoded.type !== 'refresh') throw new Error('Tipo inválido');
// Em produção: verificar se refresh token não foi revogado (blacklist/DB)
return jwt.sign(
{ sub: decoded.sub, type: 'access' },
ACCESS_SECRET,
{ expiresIn: '15m' }
);
} catch (err) {
throw new Error('Refresh token inválido ou expirado');
}
}
JSON vs Protobuf: Performance
Protocol Buffers (protobuf) é uma alternativa binária ao JSON para alta performance.
# Comparação de tamanho
echo "JSON: dados.json"
python -c "
import json, sys
dados = {'usuarios': [{'id': i, 'nome': f'Usuario{i}', 'email': f'user{i}@exemplo.com'} for i in range(1000)]}
with open('dados.json', 'w') as f: json.dump(dados, f)
print(f'{sys.getsizeof(json.dumps(dados))} bytes em memória')
"
# Teste de performance JSON vs Protobuf
python -c "
import json, time, sys
dados = {'usuarios': [{'id': i, 'nome': f'User{i}', 'email': f'user{i}@test.com'} for i in range(10000)]}
# Serialização JSON
start = time.time()
for _ in range(100):
s = json.dumps(dados)
json_time = time.time() - start
# Desserialização JSON
start = time.time()
for _ in range(100):
json.loads(s)
json_load = time.time() - start
print(f'JSON serialize: {json_time:.2f}s, deserialize: {json_load:.2f}s')
print(f'Tamanho: {len(s)} bytes')
"
Lab: Implementação JWT Completa
# 1. Gerar chave secreta
SECRET=$(python -c "import secrets; print(secrets.token_hex(32))")
echo "SECRET=$SECRET"
# 2. Gerar JWT com Python
cat << 'EOF' > jwt-gen.py
import jwt
import json
from datetime import datetime, timedelta
SECRET = "$SECRET"
payload = {
"sub": "user-001",
"name": "Admin Sistema",
"role": "admin",
"permissions": ["read:users", "write:users", "delete:users"],
"iat": datetime.utcnow(),
"exp": datetime.utcnow() + timedelta(minutes=30),
"iss": "https://auth.kb.exemplo"
}
token = jwt.encode(payload, SECRET, algorithm="HS256")
print(f"TOKEN: {token}")
# Decodificar sem validar (apenas para debug)
header = jwt.get_unverified_header(token)
decoded = jwt.decode(token, options={"verify_signature": False})
print(f"\nHEADER: {json.dumps(header, indent=2)}")
print(f"PAYLOAD: {json.dumps(decoded, indent=2, default=str)}")
EOF
python jwt-gen.py
# 3. Pipeline: validar JWT em API
cat << 'EOF' > jwt-validate.py
import jwt
import sys
SECRET = "$SECRET"
def validate_token(token):
try:
decoded = jwt.decode(
token, SECRET,
algorithms=["HS256"],
issuer="https://auth.kb.exemplo"
)
return {"valid": True, "user": decoded.get("name"), "role": decoded.get("role")}
except jwt.ExpiredSignatureError:
return {"valid": False, "error": "Token expirado"}
except jwt.InvalidIssuerError:
return {"valid": False, "error": "Issuer inválido"}
except jwt.InvalidTokenError as e:
return {"valid": False, "error": str(e)}
# Simular requisição
token = sys.argv[1] if len(sys.argv) > 1 else ""
result = validate_token(token)
print(json.dumps(result, indent=2))
EOF
python jwt-validate.py "SEU_TOKEN_AQUI"
JWT é o padrão de autenticação stateless mais adotado na web — mas a segurança depende de implementação correta: algoritmo forçado, expiration, e secrets fortes.