Pular para o conteúdo
ecdsa.com
Ruby

ruby-jwt (jwt gem)

A ruby-jwt, a gem jwt usada pela maioria das aplicações Ruby e Rails: um algorithm explícito é obrigatório, os tipos de chave são conferidos e o ES256K está incluído — mas o EdDSA passou para uma gem separada na 3.x, e segredos HMAC curtos são aceitos sem nenhum aviso.
Licença
MIT
Versão verificada
3.3.0 ·
Código-fonte
github.com/jwt/ruby-jwt
Documentação
github.com/jwt/ruby-jwt#readme

Visão geral da compatibilidade

JOSE e JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

JWT.encode e JWT.decode cobrem HS, RS, PS e ES em 256/384/512 e o ES256K, sobre a gem openssl do Ruby. O EdDSA não faz mais parte da gem jwt: com apenas a jwt 3.3.0 carregada, a codificação com EdDSA falhou com Unsupported signing method e passou a funcionar depois de adicionar a jwt-eddsa — cuja dependência nativa ed25519 precisa de um compilador C na instalação, algo que imagens de contêiner enxutas não têm.

A verificação exige indicar o algoritmo: JWT.decode(token, key, true) sem algorithm: falhou com Expected a different algorithm. Tokens sem assinatura são recusados na verificação e só podem ser lidos com a verificação desativada (JWT.decode(token, nil, false)), o que o código nunca deve fazer com tokens em que confia. Os tipos de chave são conferidos: um token HS256 forjado com o texto PEM da chave EC falhou com HMAC key expected to be a String quando a chave de verificação era o objeto da chave EC — mas, com a string PEM passada como chave HS256, o token foi verificado; por isso, mantenha as chaves como objetos OpenSSL::PKey. O ES256 recusa uma chave P-384 no momento da assinatura.

As verificações de claims precisam ser ativadas explicitamente: não há tolerância, e exp é opcional, a menos que esteja listada em required_claims, como o exemplo faz junto com o emissor. Segredos HMAC de qualquer tamanho são aceitos — uma chave de 5 bytes assinou e verificou sem nenhum aviso —, então teste os segredos com o verificador de segredos JWT.

As assinaturas ES256 são 64 bytes de r‖s brutos; uma assinatura DER falha com Signature verification failed. Cole um token no depurador de JWT para ver qual codificação ele usa.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/ruby-jwt/run.sh executou ruby-jwt (jwt gem) 3.3.0 na imagem de contêiner ruby:4.0.7-slim-trixie em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Algoritmos JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

    Assinatura e verificação funcionaram com HS, RS, PS e ES em 256/384/512 e ES256K na gem jwt; o EdDSA só depois de adicionar a gem jwt-eddsa.

    Registrado pela verificação
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, ES256K, EdDSA
    evidence jws-algorithms: EdDSA with only the jwt gem loaded: JWT::EncodeError: Unsupported signing method; after require "jwt/eddsa": ok
  • Codificação da assinatura ES256

    raw

    As assinaturas ES256 são 64 bytes de r‖s brutos.

    Registrado pela verificação
    fact jws-ecdsa-encoding = raw
  • Assinatura DER dentro de um JWS

    rejected

    Uma assinatura ES256 codificada em DER falha com JWT::VerificationError: Signature verification failed.

    Registrado pela verificação
    fact jws-der-signature = rejected
    evidence jws-der-signature: JWT::VerificationError: Signature verification failed
  • Lista de algoritmos permitidos

    required

    Verificar sem algorithm: falha com JWT::IncorrectAlgorithm: Expected a different algorithm.

    Registrado pela verificação
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: JWT.decode with verify true and no algorithm option: JWT::IncorrectAlgorithm: Expected a different algorithm
  • Tokens sem assinatura (alg none)

    rejected

    Tokens sem assinatura falham na verificação com Expected a different algorithm; só verify = false permite lê-los.

    Registrado pela verificação
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: verify true, algorithm ES256: JWT::IncorrectAlgorithm: Expected a different algorithm; verify false: ok
  • Chave pública usada como segredo HMAC

    rejected

    Um token HS256 que usava o texto PEM como chave falhou contra o objeto da chave EC com HMAC key expected to be a String, mas foi verificado quando a própria string PEM foi passada como chave HS256.

    Registrado pela verificação
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: algorithms [ES256, HS256] with the EC key object: JWT::VerificationKeyError: HMAC key expected to be a String
    evidence jwt-hmac-public-key: algorithm HS256 with the PEM string as key: ok
  • Curva conferida com o alg

    enforced

    Assinar ES256 com uma chave P-384 falha com payload algorithm is ES256 but ES384 signing key was provided.

    Registrado pela verificação
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: JWT::EncodeError: payload algorithm is ES256 but ES384 signing key was provided
  • Tolerância padrão de diferença de relógio

    0s

    Sem tolerância por padrão: um token que tinha expirado três segundos antes foi rejeitado.

    Registrado pela verificação
    fact jwt-clock-skew = 0s
  • Claim de expiração obrigatória

    no

    Um token sem exp foi decodificado; required_claims: ["exp"] o faz falhar com Missing required claim exp.

    Registrado pela verificação
    fact jwt-exp-required = no
    evidence jwt-exp-required: with required_claims: ["exp"]: JWT::MissingRequiredClaim: Missing required claim exp
  • Segredo HMAC curto

    accepted

    Um segredo HS256 de 5 bytes funcionou na assinatura e na verificação sem nenhum aviso.

    Registrado pela verificação
    fact jwt-hmac-short-secret = accepted
    evidence jwt-hmac-short-secret: 5-byte HS256 key: ok

Uso

Codifique um token ES256 e decodifique-o com o algoritmo indicado, as claims exp e iss obrigatórias e 30 segundos de tolerância.
require "jwt"

key = OpenSSL::PKey::EC.generate("prime256v1")
public_key = OpenSSL::PKey.read(key.public_to_pem)
payload = { "sub" => "user-42", "iss" => "https://auth.example.com", "exp" => Time.now.to_i + 600 }
token = JWT.encode(payload, key, "ES256")

# Name the algorithm, require exp and iss, and allow some clock drift.
claims, header = JWT.decode(token, public_key, true,
                            algorithm: "ES256",
                            required_claims: %w[exp iss],
                            iss: "https://auth.example.com", verify_iss: true,
                            leeway: 30)
puts "sub: #{claims['sub']}, alg: #{header['alg']}"

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner ruby:4.0.7-slim-trixie, e a execução falha se a saída não corresponder.

Configuração
gem install --silent --no-document jwt -v 3.3.0
Comando
ruby usage.rb
A saída deve corresponder a (expressão regular)
sub: user-42, alg: ES256

Bibliotecas