Quando decodifico um JWT e vejo:
{
"alg": "none"
}
paro de tratá-lo como um token assinado normal. alg: none representa um JWS não protegido por assinatura ou MAC. O cabeçalho e o payload podem continuar legíveis, mas não existe integridade criptográfica no próprio JWS.
Minha primeira pergunta é:
A aplicação definiu explicitamente este contexto como um caso em que JWT sem assinatura é permitido?
Se a resposta é não, rejeito.
O que alg: none significa
A RFC 7518 define none como o algoritmo Unsecured JWS. A RFC 7519 também mostra um exemplo de Unsecured JWT.
No formato compacto, ainda existem três segmentos:
header.payload.
O segmento de assinatura está vazio.
Consigo ler cabeçalho e payload no Decodificador JWT, mas marco o resultado como dados decodificados, não como claims autenticados.
Por que o header não pode definir minha política
Um padrão perigoso seria:
if (header.alg === "none") {
acceptWithoutSignature();
}
Não projeto verificadores assim.
Antes da autenticação, o JWT inteiro é entrada não confiável. Se deixo header.alg decidir se assinatura é necessária, dou ao token controle sobre a política que deveria protegê-lo.
Prefiro configuração explícita:
const allowedAlgorithms = ["RS256"];
E exijo que o algoritmo recebido esteja dentro desse conjunto.
A RFC 8725 recomenda que bibliotecas permitam ao chamador definir algoritmos aceitos e não processem outros algoritmos automaticamente.
A falha histórica que quero evitar
O problema clássico com alg: none não era a existência do formato em si. Era a combinação de implementações permissivas com a ideia de que o algoritmo informado pelo token poderia determinar como autenticar o próprio token.
Um token assinado podia ser modificado conceitualmente para:
- trocar
algparanone; - alterar claims;
- remover a assinatura;
- depender de um verifier quebrado que aceitasse o modo sem assinatura.
O controle correto está na allowlist da aplicação, não em “detectar payload suspeito” depois.
Decodificar continua sendo possível
Um JWT sem assinatura pode carregar:
{
"sub": "user_123",
"role": "admin",
"exp": 1789203600
}
Esses valores mostram o que o token afirma. Sem assinatura/MAC, qualquer pessoa capaz de construir o token pode escolher esses claims.
Por isso, nunca uso:
payload parseou
→ role parece válida
→ usuário autenticado
Se o fluxo exige autenticação, aplico as proteções criptográficas definidas pela aplicação.
none não é universalmente proibido pelo formato
A RFC 7518 reconhece Unsecured JWS. A RFC 8725 observa que none pode existir em contextos explicitamente protegidos por outro mecanismo, mas geração/consumo desse modo deve ser intencional.
Essa palavra — explicitamente — é a parte que considero essencial.
Não crio uma opção global como:
allowUnsignedJwt = true
para uma aplicação que recebe vários tipos de JWT.
Se há um perfil legítimo que usa JWT sem assinatura porque a integridade está garantida em outra camada, isolo esse perfil com regras próprias e mutuamente exclusivas das regras de autenticação de JWT assinado.
Como configuro um verifier na prática
A API exata depende da biblioteca, mas a política que procuro é semelhante a:
verifyJwt(token, trustedKey, {
algorithms: ["RS256"],
issuer: "https://login.example.com/",
audience: "api://orders"
});
O algoritmo permitido vem da configuração confiável. Se chega alg: none, a biblioteca deve rejeitar salvo se aquele modo foi habilitado explicitamente para o contexto correto.
alg none não é o mesmo que assinatura inválida
São estados diferentes.
Assinatura inválida: existe uma assinatura, mas ela não verifica com a chave esperada.
alg: none: o token declara que não usa algoritmo de assinatura e o segmento de assinatura fica vazio.
Se um fluxo que normalmente produz RS256 passa a gerar none, investigo a origem do token e a configuração do issuer imediatamente. Tentar outra chave pública não resolve um token que não possui assinatura.
Também valido o restante da política
Mesmo usando um algoritmo permitido, ainda verifico:
- assinatura;
- issuer confiável;
- audience;
expenbf;- tipo/profile do token;
- scopes e roles exigidos.
O Validador JWT mostra essa separação. O Verificador de Assinatura JWT trabalha apenas com algoritmos suportados explicitamente em vez de executar qualquer alg arbitrário.
Se alg: none aparecer inesperadamente em produção
Meu checklist é:
- Decodifico e confirmo
"alg":"none". - Confirmo que o segmento de assinatura está vazio.
- Identifico qual serviço criou o token.
- Verifico qual algoritmo a aplicação espera em configuração confiável.
- Confiro se a biblioteca tem allowlist explícita.
- Rejeito
nonese não estiver permitido para aquele perfil. - Procuro fallbacks que pulem verificação.
- Reviso logs por mudanças inesperadas de algoritmo.
Não “conserto” o token trocando none para RS256. Mudar o header não cria uma assinatura válida.
Minha regra é direta: a aplicação decide se um JWT sem assinatura é permitido; o próprio token nunca recebe autoridade para tomar essa decisão.