JWT (JSON Web Token) é um formato compacto para transferir dados entre partes como um objeto JSON protegido por uma assinatura digital. O token pode ser assinado usando HMAC (chave simétrica) ou RSA/ECDSA (par assimétrico), garantindo a integridade e autenticidade dos dados. De acordo com IETF RFC 7519, 2015, JWT é usado em milhões de aplicações para autenticação, troca segura de claims e como formato de ID Token no OpenID Connect.
Pontos Principais
JSON Web Token (JWT) é um padrão aberto (RFC 7519) que define uma maneira compacta e autocontida de transmitir informações entre partes como um objeto JSON. As informações no JWT são chamadas de claims — afirmações sobre o sujeito (usuário) e atributos adicionais. Cada claim é um par chave-valor: identificador do usuário, função, tempo de expiração, emissor.
JWT é chamado de autocontido porque todas as informações necessárias para verificação estão dentro do próprio token. O servidor não precisa acessar um banco de dados ou armazenamento externo para verificar a validade do token — basta verificar a assinatura. Essa propriedade torna o JWT ideal para sistemas distribuídos e arquiteturas de microsserviços, onde múltiplos serviços precisam autenticar requisições sem um armazenamento de sessão compartilhado.
De acordo com Auth0, 2025, mais de 65% das aplicações móveis e web usam JWT como formato principal de token para autenticação de API, superando tokens opacos e identificadores de sessão.
JWT consiste em três partes separadas por pontos: header.payload.signature. Cada parte é um JSON codificado em Base64url. Vamos examinar cada parte em detalhes.
Header contém dois campos obrigatórios: alg (algorithm — algoritmo de assinatura) e typ (type — tipo de token, sempre “JWT”). O algoritmo pode ser simétrico (HS256 — HMAC com SHA-256) ou assimétrico (RS256 — RSA com SHA-256, ES256 — ECDSA com P-256). Algoritmos assimétricos são preferíveis porque permitem ao cliente verificar a assinatura sem possuir a chave secreta.
Exemplo de um header decodificado:
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-id-1"
}
Payload contém claims — afirmações sobre o sujeito. Os claims são divididos em três tipos: registrados (iss, sub, aud, exp, nbf, iat, jti), públicos (definidos pelo desenvolvedor no Registro IANA) e privados (acordados entre as partes). sub (subject) é o identificador único do usuário. exp (expiration) é o timestamp de expiração do token. iss (issuer) é o emissor do token.
{
"sub": "user-abc-123",
"iss": "https://auth.example.com",
"aud": "my-mobile-app",
"exp": 1812345678,
"iat": 1812342078,
"role": "premium_user"
}
Signature é criada aplicando o algoritmo de assinatura à concatenação do header e payload usando uma chave secreta ou privada. A fórmula: HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) para HMAC, ou RSASHA256(...) para algoritmo assimétrico. O destinatário calcula a assinatura da mesma forma e a compara com a recebida — se coincidirem, os dados não foram alterados.
O fluxo de trabalho com JWT consiste em duas fases: criação (emissão) do token pelo servidor de autenticação e verificação do token pelo cliente ou servidor de recursos. O servidor de autenticação recebe as credenciais do usuário, cria um payload com claims e o assina. O JWT resultante é enviado ao cliente em resposta a uma solicitação de login ou no corpo de resposta do OAuth 2.0 / OpenID Connect.
Em aplicações móveis, JWT é usado da seguinte forma: após um login bem-sucedido, o usuário recebe um token de acesso no formato JWT. A aplicação o armazena em um armazenamento seguro (Keychain no iOS, EncryptedSharedPreferences no Android). A cada requisição à API, a aplicação adiciona o cabeçalho Authorization: Bearer <token>. O servidor da API verifica a assinatura do JWT, extrai os claims e toma decisões de acesso com base neles — sem consultar o banco de dados.
De acordo com Google Codelabs, 2025, o uso de JWT no Firebase Authentication reduz o número de requisições ao servidor de autenticação em 40–60% em comparação com tokens de sessão, pois os dados são verificados localmente em cada microsserviço. Isso é especialmente importante para arquiteturas de alta carga, onde cada milissegundo de latência impacta a experiência do usuário. Com 50.000 requisições por minuto, mudar para JWT pode economizar até 10 instâncias de servidor que lidam com requisições de introspecção.
JWT e Session Token resolvem o mesmo problema — autenticação de requisições — mas diferem fundamentalmente em arquitetura. Session Token é uma string de identificação aleatória que referencia dados de sessão armazenados no servidor (stateful). JWT é um token autocontido que contém todos os dados dentro de si (stateless).
| Parâmetro | JWT | Session Token |
|---|---|---|
| Armazenamento de dados | Dentro do token (autocontido) | No servidor (armazenamento de sessão) |
| Escalabilidade | Não requer armazenamento compartilhado | Requer Redis/BD para multi-servidor |
| Revogação de token | Complexa (precisa de blacklist) | Simples (excluir sessão do BD) |
| Tamanho | Grande (500–2000 bytes) | Pequeno (16–64 bytes) |
| Verificação de assinatura | Criptográfica | Nenhuma (comparação de strings) |
JWT vence em sistemas distribuídos: microsserviços podem verificar o token localmente sem um armazenamento compartilhado. Por exemplo, em uma arquitetura com cinco microsserviços, cada serviço verifica o JWT em 1–2 ms sem uma chamada de rede, enquanto um session token requer uma consulta centralizada ao Redis a cada requisição, adicionando 10–30 ms de latência. No entanto, JWT é difícil de revogar — uma vez emitido, é válido até expirar. Session Token é fácil de revogar excluindo o registro do BD ou Redis.
Para aplicações móveis, uma abordagem combinada — JWT com vida útil curta (15–30 minutos) e Refresh Token — oferece um equilíbrio entre desempenho e segurança. JWT é usado para acesso à API, enquanto o refresh token (geralmente opaco) é usado para obter novos JWTs. Se um JWT for comprometido, o invasor tem acesso por 15–30 minutos; se um refresh token for comprometido, a sessão é bloqueada através de rotação e detecção de reutilização.
A segurança do JWT depende de uma implementação correta. A vulnerabilidade mais comum é o ataque “alg none”: um invasor altera o header do token para “alg”: “none”, e o servidor, sem verificar o algoritmo, aceita o token falsificado. Proteção: sempre verifique se o algoritmo no header corresponde ao esperado (RS256, ES256) e rejeite tokens com alg: none.
As vulnerabilidades do JWT também incluem: chave secreta fraca para HMAC (descoberta em minutos), vazamento de chave privada (assinar qualquer dados em nome do servidor), armazenamento de dados sensíveis no payload (JWT não criptografa, apenas assina), ataque de injeção JWK header (injeção de uma chave pública personalizada). O uso de bibliotecas confiáveis — Nimbus JOSE + JWT, jjwt (io.jsonwebtoken), PyJWT — reduz o risco de exploração dessas vulnerabilidades.
Uma medida de segurança adicional é o JWK Thumbprint (RFC 7638): vinculação de uma chave pública ao token através de uma impressão digital no header. Se o servidor armazenar a impressão digital esperada para cada cliente, a injeção JWK header se torna impossível — o servidor rejeita qualquer chave que não corresponda à registrada. O OAuth Security Workshop 2025 recomenda JWK Thumbprint como proteção obrigatória para todos os JWTs usados em aplicações financeiras e médicas.
A biblioteca jjwt (auth0/java-jwt) permite criar e verificar JWTs em uma aplicação Android em apenas algumas linhas. No exemplo abaixo, o servidor gera um token com sub e role, e o cliente verifica a assinatura. Para armazenamento seguro da chave secreta no servidor, use variáveis de ambiente ou um HSM (Hardware Security Module) — armazenar a chave no código ou em um arquivo de configuração é um grave erro de segurança.
val secret = "my-256-bit-secret-key-here"
val token = JWT.create()
.withSubject("user-abc-123")
.withIssuer("auth.example.com")
.withClaim("role", "premium_user")
.withExpiresAt(Date(System.currentTimeMillis() + 3600000))
.sign(Algorithm.HMAC256(secret))
// Enviando token para o cliente
println("JWT: $token")
fun verifyToken(token: String): Boolean {
return try {
val decoded = JWT.require(Algorithm.HMAC256(secret))
.withIssuer("auth.example.com")
.build()
.verify(token)
// Assinatura válida, claims extraídos
println("Subject: ${decoded.subject}")
true
} catch (e: Exception) {
println("Token inválido: ${e.message}")
false
}
}
Perguntas Frequentes
Não. JWT é assinado, não criptografado — qualquer um pode decodificar o payload Base64 e ler os dados. Informações sensíveis (senhas, números de cartão, dados pessoais) devem ser transmitidas apenas de forma criptografada usando JWE (JSON Web Encryption).
Recomenda-se ES256 (ECDSA com P-256) — ele fornece um nível de segurança equivalente ao RSA 2048-bit com um tamanho de assinatura significativamente menor. RS256 é adequado para compatibilidade com sistemas legados. HS256 (HMAC) requer troca segura de chave secreta, o que é mais desafiador em uma arquitetura distribuída.
JWT não pode ser revogado diretamente — é válido até exp. Soluções: usar vida útil curta (15–30 minutos), manter uma blacklist de jti (JWT ID) revogados no servidor, ou vincular tokens a uma versão de chave secreta. O refresh token é revogado da forma padrão — removendo-o do armazenamento.
Bearer token é um conceito: qualquer token que o portador pode usar para acesso. JWT é um formato de token específico. Um Bearer token pode ser um JWT ou uma string opaca. JWT adiciona autocontenção e verificação criptográfica ao conceito Bearer.
Um JWT típico com assinatura RS256 ocupa 500–2000 bytes. Se o payload contiver muitos claims personalizados ou uma assinatura assimétrica com chave grande for usada, o tamanho pode chegar a 4–5 KB. Isso é significativamente maior que um session token (16–64 bytes), afetando o tamanho dos cabeçalhos HTTP.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também