JWT exp/iat/nbf Erros: Bugs de reivindicação de tempo que quebram Auth

JWT — exp, iat e nbf — são aparentemente simples. Eles são apenas carimbos de data/hora Unix. No entanto, eles causam alguns dos bugs de autenticação mais frustrantes na produção: tokens que expiram muito cedo, tokens que funcionam em um servidor, mas não em outro, e tokens que nunca expiram.
Este artigo aborda os bugs mais comuns de reivindicação de tempo, por que eles acontecem e como corrigi-los com padrões seguros.
Como funcionam exp, iat e nbf
Cada JWT pode transportar três declarações relacionadas ao tempo em sua carga útil:
- exp (Tempo de Expiração) — o token NÃO DEVE ser aceito após este timestamp
- iat (emitido em) — quando o token foi criado
- nbf (Não antes) — o token NÃO DEVE ser aceito antes deste carimbo de data/hora
Todos os três são datas numéricas: o número de segundos desde 1970-01-01T00:00:00Z (época Unix). Não milissegundos – segundos. Somente essa distinção causa cerca de 20% de todos os bugs de tempo do JWT.
Use nosso decodificador JWT para inspecionar declarações de tempo em qualquer token instantaneamente.
Bug nº 1: usando milissegundos em vez de segundos
JavaScript Date.now() retorna milissegundos. A especificação JWT requer segundos. Definir exp como Date.now() + 3600000 cria um token que expira no ano de 2089, não em uma hora.
// ERRADO — milissegundos
const exp = Date.now() + 3600000; // CORRETO — segundos
const exp = Math.floor(Data.now() / 1000) + 3600;A maioria das bibliotecas JWT lidam com isso internamente, mas se você estiver criando cargas manualmente, esta é a primeira coisa a verificar.
Bug nº 2: reivindicação de exp totalmente ausente
Se você esquecer de definir exp, muitas bibliotecas criarão com prazer um token que nunca expira. Este é um risco de segurança: um token vazado permanece válido para sempre.
Sempre defina exp. Sempre valide-o no servidor. Se sua biblioteca não rejeita tokens sem exp por padrão, configure-a para fazer isso.
// Node.js jsonwebtoken - impor expiração
jwt.verify(token, segredo, { maxAge: '1h' });Bug nº 3: distorção do relógio entre servidores
O servidor A emite um token às 14:00:00. O relógio do servidor B marca 13:59:55 (5 segundos atrasado). Se o token tiver nbf: 1711540800 (14:00:00), o Servidor B o rejeitará como "ainda não válido."
Isso é especialmente comum em:
- Arquiteturas de microsserviços com relógios não sincronizados
- Funções sem servidor onde os contêineres apresentam desvio de clock
- Aplicativos móveis em que o relógio do dispositivo é definido manualmente
A correção
Permite uma pequena tolerância de relógio (também chamada de "margem de manobra") — normalmente 30–60 segundos:
//jsonwebtoken
jwt.verify(token, segredo, { clockTolerance: 30 }); // José (Node.js)
aguarde jwtVerify(token, chave, { clockTolerance: '30s' });Nunca defina a tolerância acima de 2 minutos. Se precisar de mais, corrija sua sincronização NTP.
Bug nº 4: pedido de validação errado
A ordem de validação correta é importante. Se você verificar a assinatura após verificar a expiração, um invasor poderá criar um token com um exp futuro que passe na verificação de tempo, mas tenha uma assinatura inválida.
Pedido de validação seguro:
- Decodificar cabeçalho (algoritmo de verificação)
- Verificar assinatura
- Verifique
exp(rejeite se expirar) - Verifique
nbf(rejeite se ainda não for válido) - Verifique
iat(rejeite se for excessivamente antigo) - Verifique o emissor, o público e outras reivindicações
A maioria das bibliotecas bem mantidas lidam com isso corretamente, mas o middleware personalizado geralmente erra.
Bug nº 5: confusão de fuso horário em iat
Os carimbos de data/hora JWT são sempre UTC. Mas às vezes os desenvolvedores os criam usando a hora local:
// ERRADO — fuso horário local
const iat = new Date('2026-03-27T14:00:00').getTime() / 1000; // CORRETO — UTC explícito
const iat = new Date('2026-03-27T14:00:00Z').getTime() / 1000;Sem o sufixo Z, o JavaScript interpreta a string no fuso horário local, o que pode mudar o carimbo de data/hora em horas.
Bug #6: Aceitando tokens sem verificação nbf
A declaração nbf é útil para tokens de ativação atrasada — por exemplo, um token que só deve funcionar após uma implantação agendada. Se o seu validador ignorar nbf, esses tokens poderão ser usados antes do tempo de ativação pretendido.
A maioria das bibliotecas valida nbf por padrão, mas verifique isso em sua configuração, especialmente com middleware personalizado.
Bug nº 7: Expiração excessivamente longa
Definir exp para 30 dias para um token de acesso anula a finalidade dos tokens de curta duração. Melhores práticas:
- Tokens de acesso: 5–15 minutos
- Atualizar tokens: 7–30 dias (com rotação)
- ID tokens: 1 hora
Para padrões de token de atualização seguros, consulte nosso guia sobre Rotação de token de atualização JWT.
Casos de teste que toda API deveria ter
Adicione-os ao seu conjunto de testes para detectar bugs de reivindicação de tempo antecipadamente:
- Token com
expno passado → 401 - Token com
expexatamente agora → 401 (limite) - Token sem
exp→ 401 - Token com
nbfno futuro → 401 - Token com
nbfligeiramente no futuro (dentro da tolerância) → 200 - Token com
iatno futuro → 401 (indica adulteração) - Token com carimbos de data e hora em milissegundos → 401 (detecta o bug ms/s)
Padrões seguros para estruturas populares
Node.js (jsonwebtoken)
jwt.sign(carga útil, segredo, { expiresIn: '15m' });
jwt.verify(token, segredo, { clockTolerance: 30, maxAge: '15m'
});Python (PyJWT)
jwt.decode(token, chave, algoritmos=['HS256'], margem de manobra = timedelta (segundos = 30), opções={'require': ['exp', 'iat']})Vá (golang-jwt)
parser := jwt.NewParser( jwt.WithLeeway(30 * tempo.Segundo), jwt.WithValidMethods([]string{"HS256"}),
)FAQ
Isso deveria ser obrigatório?
Embora a especificação JWT diga que iat é opcional, torná-lo obrigatório ajuda na depuração e nas trilhas de auditoria. Sem iat, você não pode determinar quando um token foi criado, dificultando a correlação com eventos de segurança.
Quanto desvio do relógio devo permitir?
Um padrão seguro comum é de 30 a 60 segundos. Mais de 2 minutos apresentam risco de segurança. Se seus sistemas precisarem de mais, corrija a sincronização NTP em vez de ampliar a margem de distorção.
Qual código de status HTTP devo retornar para um token expirado?
Return 401 Unauthorized com um corpo de erro claro como {"error": "token_expired"}. Não retorne 403 Forbidden — isso implica que o token é válido, mas não possui permissões, o que é uma situação diferente.
Ferramentas e artigos relacionados
- Decodificador JWT — inspeciona as declarações de tempo em qualquer token
- Erros de segurança do JWT — armadilhas mais amplas do JWT além das reivindicações de tempo
- Refresh Token Rotation — padrão seguro para sessões de longa duração
- 2FA Guide — adicione uma segunda camada além dos tokens