本文へスキップ
ecdsa.com
Rust

jsonwebtoken (Rust)

Vincent Prouillet による jsonwebtoken クレートは、Rust で最も使われている JWT ライブラリです。Algorithm は none を含まない閉じた列挙型で、exp は必須、時刻のずれの許容(leeway)はデフォルトで 60 秒です。さらにバージョン 10 以降は、暗号バックエンドを自分で選ぶ必要があります。
ライセンス
MIT
確認したバージョン
11.1.0 ·
ソースコード
github.com/Keats/jsonwebtoken
ドキュメント
docs.rs/jsonwebtoken/11.1.0/jsonwebtoken/

互換性の概要

JOSE と JWT

JWS アルゴリズム
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384EdDSA

jsonwebtoken は、encode、decode、Validation オブジェクトを使って JWT をエンコード・デコードし、クレームには serde を使います。バージョン 10 以降、暗号処理はフィーチャーフラグで選ぶバックエンド(aws_lc_rs または rust_crypto)が担い、チェックでは aws_lc_rs を使いました。フィーチャーを指定し忘れてもビルドは失敗しません。デフォルトのフィーチャーだけを使うと、11.1.0 はコンパイルに成功したうえで、最初の encode() で Could not automatically determine the process-level CryptoProvider というパニックを起こしました。サンプルのマニフェストのように、バックエンドをちょうど 1 つだけ有効にしてください。

アルゴリズムは閉じた列挙型です。256/384/512 の HS、RS、PS と ES256、ES384、EdDSA はすべて署名と検証ができましたが、ES512、ES256K、Ed25519 はそもそもバリアントとして存在しません。none のバリアントもないため、署名のないトークンはヘッダーの解析中に unknown variant none で失敗します。Validation::new(alg) が許可リストです。Validation::new(ES256) でチェックした HS256 トークンは InvalidAlgorithm で失敗しました。両方のアルゴリズムを許可した場合でも、EC 公開鍵の PEM テキストを鍵とした HS256 トークンは InvalidKeyFormat で失敗しました。EC の DecodingKey は HMAC シークレットとして機能できないためです。

クレームのチェックはほとんどのライブラリより厳格です。exp のないトークンは Missing required claim: exp で失敗します。一方、時刻のずれの許容はデフォルトで 60 秒で、57 秒前に期限切れになったトークンも検証を通りました。サンプルのように leeway を小さくし、Validation に発行者とオーディエンスを設定してください。ES256 は署名時に P-384 の鍵を InvalidEcdsaKey で拒否します。

ES256 の署名は JWS が求める 64 バイトの生の r‖s で、DER の署名は InvalidSignature で失敗します。鍵は PEM(from_ec_pem、from_rsa_pem、from_ed_pem)と DER から読み込めます。Ed25519 の DER では、エンコード用の鍵は PKCS#8 を、デコード用の鍵は 32 バイトの生の公開鍵を受け取りました。トークンは JWT デバッガーで解析し、HMAC シークレットは JWT シークレットチェッカーで確認してください。

JWT 以外の署名には、バックエンドのクレート(aws-lc-rs または RustCrypto ecdsa)を直接使ってください。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/jsonwebtoken-rust/run.sh がコンテナイメージ rust:1.99.1-trixie 内で jsonwebtoken (Rust) 11.1.0 を実行しました。このページの内容はその出力と一致していなければならず、各事実には該当する出力行を示しています。

  • JWS アルゴリズム

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384EdDSA

    256/384/512 の HS、RS、PS と ES256、ES384、EdDSA で署名と検証ができました。ES512、ES256K、Ed25519 は Algorithm のバリアントにありません。

    チェックによる記録
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, EdDSA
    evidence jws-algorithms: ES512: not an Algorithm variant
    evidence jws-algorithms: ES256K: not an Algorithm variant
    evidence jws-algorithms: Ed25519: not an Algorithm variant
  • ES256 署名のエンコーディング

    raw

    ES256 の署名は 64 バイトの生の r‖s です。

    チェックによる記録
    fact jws-ecdsa-encoding = raw
  • JWS 内の DER 署名

    rejected

    DER エンコードの ES256 署名は InvalidSignature で失敗します。

    チェックによる記録
    fact jws-der-signature = rejected
    evidence jws-der-signature: Err("InvalidSignature")
  • アルゴリズムの許可リスト

    required

    Validation::new(alg) で許可するアルゴリズムを設定します。Validation::new(ES256) でチェックした HS256 トークンは InvalidAlgorithm で失敗しました。

    チェックによる記録
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: HS256 token with Validation::new(ES256): Err("InvalidAlgorithm")
  • 署名なしトークン(alg none)

    rejected

    none は Algorithm のバリアントではありません。ヘッダーの解析が unknown variant none で失敗します。

    チェックによる記録
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: Err("JSON error: unknown variant `none`, expected one of `HS256`, `HS384`, `HS512`, `ES256`, `ES384`, `RS256`, `RS384`, `RS512`, `PS256`, `PS384`, `PS512`, `EdDSA` at line 1 column 13"); "none" parses as Algorithm: false
  • HMAC シークレットとして使われる公開鍵

    rejected

    ES256 と HS256 の両方を許可した状態でも、PEM テキストを鍵とした HS256 トークンは EC の DecodingKey に対して InvalidKeyFormat で失敗しました。

    チェックによる記録
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: algorithms [ES256, HS256], EC DecodingKey: Err("InvalidKeyFormat")
  • alg と曲線の照合

    enforced

    P-384 の鍵で ES256 の署名を行うと InvalidEcdsaKey で失敗します。

    チェックによる記録
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: Ok(Err("InvalidEcdsaKey"))
  • デフォルトのクロックスキュー

    60s

    Validation::new は leeway を 60 秒に設定します。57 秒前に期限切れになったトークンは検証を通り、63 秒前に期限切れになったトークンは通りませんでした。

    チェックによる記録
    fact jwt-clock-skew = 60s
    evidence jwt-clock-skew: Validation::new(HS256).leeway = 60
  • 有効期限クレームの要否

    yes

    exp のないトークンは Missing required claim: exp で失敗します。

    チェックによる記録
    fact jwt-exp-required = yes
    evidence jwt-exp-required: Err("Missing required claim: exp")
  • 暗号バックエンドを選択しない場合

    runtime-panic

    aws_lc_rs と rust_crypto のどちらも有効にしないと、クレートはコンパイルに成功し、最初の encode() で Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features. というパニックが発生しました。

    チェックによる記録
    fact crypto-provider-without-feature = runtime-panic
    evidence crypto-provider-without-feature: default features compile; the first encode() panics with: Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features.

使い方

HS256 トークンを発行し、アルゴリズム、発行者、30 秒の時刻のずれの許容を明示的に設定して検証します。
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Serialize, Deserialize)]
struct Claims {
    sub: String,
    iss: String,
    exp: u64,
}

fn main() {
    // Choose a backend explicitly: features = ["aws_lc_rs"] or ["rust_crypto"].
    let secret = b"use at least 32 random bytes from a secrets manager";
    let exp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 600;
    let claims = Claims { sub: "user-42".into(), iss: "https://auth.example.com".into(), exp };
    let token = encode(&Header::new(Algorithm::HS256), &claims, &EncodingKey::from_secret(secret)).unwrap();

    // Validation::new(alg) is the allow-list; exp is required, leeway defaults to 60 s.
    let mut validation = Validation::new(Algorithm::HS256);
    validation.set_issuer(&["https://auth.example.com"]);
    validation.leeway = 30;
    let data = decode::<Claims>(&token, &DecodingKey::from_secret(secret), &validation).unwrap();
    println!("sub: {}", data.claims.sub);
}

スニペットのテストハーネスは、すべてのサンプルをコンテナイメージ rust:1.99.1-trixie 内でそのまま実行し、出力が一致しなければ実行は失敗します。

コマンド
cargo run --quiet --release
一致すべき出力(正規表現)
sub: user-42

ライブラリ