Aller au contenu
ecdsa.com
Ruby

ruby-jwt (jwt gem)

ruby-jwt, la gem jwt qu’utilisent la plupart des applications Ruby et Rails : un algorithm explicite est obligatoire, les types de clés sont contrôlés, ES256K est inclus, mais EdDSA est passé dans une gem distincte en 3.x et les secrets HMAC courts sont acceptés sans avertissement.
Licence
MIT
Version vérifiée
3.3.0 ·
Code source
github.com/jwt/ruby-jwt
Documentation
github.com/jwt/ruby-jwt#readme

Compatibilité en un coup d’œil

JOSE et JWT

Algorithmes JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

JWT.encode et JWT.decode couvrent HS, RS, PS et ES en 256/384/512 ainsi qu’ES256K, en s’appuyant sur la gem openssl de Ruby. EdDSA ne fait plus partie de la gem jwt : avec seulement jwt 3.3.0 chargé, l’encodage EdDSA a échoué avec Unsupported signing method, et il a fonctionné après l’ajout de jwt-eddsa, dont la dépendance native ed25519 exige un compilateur C à l’installation, ce qui manque aux images de conteneur allégées.

La vérification exige de nommer l’algorithme : JWT.decode(token, key, true) sans algorithm: a échoué avec Expected a different algorithm. Les jetons non signés sont refusés à la vérification et ne se lisent qu’en désactivant celle-ci (JWT.decode(token, nil, false)), ce qu’un code ne devrait jamais faire pour des jetons auxquels il se fie. Les types de clés sont contrôlés : un jeton HS256 forgé avec le texte PEM de la clé EC a échoué avec HMAC key expected to be a String quand la clé de vérification était l’objet clé EC, mais il a été vérifié quand la chaîne PEM était passée comme clé HS256 ; conservez donc les clés sous forme d’objets OpenSSL::PKey. ES256 refuse une clé P-384 au moment de la signature.

Les contrôles de claims sont optionnels : aucune marge de tolérance, et exp est facultatif s’il n’est pas listé dans required_claims, où l’exemple le place avec l’émetteur. Les secrets HMAC de toute longueur sont acceptés (une clé de 5 octets a signé et vérifié sans avertissement) : vérifiez donc vos secrets avec le vérificateur de secrets JWT.

Les signatures ES256 font 64 octets en r‖s brut ; une signature DER échoue avec Signature verification failed. Collez un jeton dans le débogueur JWT pour voir quel encodage il contient.

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/ruby-jwt/run.sh a exécuté ruby-jwt (jwt gem) 3.3.0 dans l’image de conteneur ruby:4.0.7-slim-trixie le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Algorithmes JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSA

    Signature et vérification réussies avec HS, RS, PS et ES en 256/384/512 et ES256K dans la gem jwt ; EdDSA seulement après ajout de la gem jwt-eddsa.

    Relevé par la vérification
    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
  • Encodage des signatures ES256

    raw

    Les signatures ES256 font 64 octets, en r‖s brut.

    Relevé par la vérification
    fact jws-ecdsa-encoding = raw
  • Signature DER dans un JWS

    rejected

    Une signature ES256 encodée en DER échoue avec JWT::VerificationError: Signature verification failed.

    Relevé par la vérification
    fact jws-der-signature = rejected
    evidence jws-der-signature: JWT::VerificationError: Signature verification failed
  • Liste d’algorithmes autorisés

    required

    La vérification sans algorithm: échoue avec JWT::IncorrectAlgorithm: Expected a different algorithm.

    Relevé par la vérification
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: JWT.decode with verify true and no algorithm option: JWT::IncorrectAlgorithm: Expected a different algorithm
  • Jetons non signés (alg none)

    rejected

    Les jetons non signés échouent à la vérification avec Expected a different algorithm ; seul verify = false permet de les lire.

    Relevé par la vérification
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: verify true, algorithm ES256: JWT::IncorrectAlgorithm: Expected a different algorithm; verify false: ok
  • Clé publique utilisée comme secret HMAC

    rejected

    Un jeton HS256 dont la clé était le texte PEM a échoué face à l’objet clé EC avec HMAC key expected to be a String, mais a été vérifié quand la chaîne PEM elle-même était passée comme clé HS256.

    Relevé par la vérification
    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
  • Courbe vérifiée par rapport à alg

    enforced

    Signer en ES256 avec une clé P-384 échoue avec payload algorithm is ES256 but ES384 signing key was provided.

    Relevé par la vérification
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: JWT::EncodeError: payload algorithm is ES256 but ES384 signing key was provided
  • Décalage d’horloge toléré par défaut

    0s

    Aucune marge de tolérance par défaut : un jeton expiré depuis trois secondes a été rejeté.

    Relevé par la vérification
    fact jwt-clock-skew = 0s
  • Claim d’expiration obligatoire

    no

    Un jeton sans exp a été décodé ; required_claims: ["exp"] le fait échouer avec Missing required claim exp.

    Relevé par la vérification
    fact jwt-exp-required = no
    evidence jwt-exp-required: with required_claims: ["exp"]: JWT::MissingRequiredClaim: Missing required claim exp
  • Secret HMAC court

    accepted

    Un secret HS256 de 5 octets a permis de signer et de vérifier sans le moindre avertissement.

    Relevé par la vérification
    fact jwt-hmac-short-secret = accepted
    evidence jwt-hmac-short-secret: 5-byte HS256 key: ok

Utilisation

Encoder un jeton ES256 et le décoder en nommant l’algorithme, avec exp et iss obligatoires et 30 secondes de tolérance.
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']}"

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur ruby:4.0.7-slim-trixie, et l’exécution échoue si la sortie ne correspond pas.

Mise en place
gem install --silent --no-document jwt -v 3.3.0
Commande
ruby usage.rb
La sortie doit correspondre à cette expression régulière
sub: user-42, alg: ES256

Bibliothèques