エラーメッセージ
Provided key is too short
別の表記
- DomainException: Provided key is too short
`JWT::encode` と `JWT::decode` の両方でスローされます。php-jwt 6.x 以前は短い鍵を受け付けていたため、このエラーは通常 7.x にアップグレードした直後に発生します。
firebase/php-jwt 7.x (PHP) — DomainException from JWT::encode / JWT::decode
このエラーの意味
php-jwt 7 は、署名や検証の前に鍵長をチェックします。HMAC シークレットにはハッシュの出力以上のビット数(HS256 では 32 バイト、HS384 では 48 バイト、HS512 では 64 バイト)が、RSA 鍵には 2048 ビット以上が、EC 鍵にはそのアルゴリズムが前提とするサイズ以上が必要です。これより短い鍵が拒否されるのは、その鍵で保護されたトークンを偽造できるからです。短い HMAC シークレットはオフラインの総当たりで破られ、1024 ビットの RSA は現在の最小要件を下回っています。
発生する理由
人が考えた短い HMAC シークレット
よくある`secret`、`change-me-please`、アプリ名のような値は 32 バイトを大きく下回り、推測できてしまう場合も少なくありません。
php-jwt の 6.x から 7.x へのアップグレード
よくあるバージョン 7 で、鍵長の最小値のチェックが追加されました。何年も問題なく動いていたコードとシークレットが、`composer update` 後の最初のリクエストで失敗します。
1024 ビットの RSA 鍵
ときどき1024 ビットで生成された(またはサンプルからコピーした)古い鍵は、RS256/RS384/RS512 では拒否されます。
修正方法
- 1.
新しいランダムなシークレットを生成する
`random_bytes` または `openssl rand` を使い、シークレットはシークレットストアに保管してください。シークレットをローテーションすると既存のトークンが無効になるため、必要に応じて新旧のシークレットを短期間併用できるよう計画してください。
bash # HS256 needs >= 32 bytes, HS512 >= 64; generate and store as base64 php -r 'echo base64_encode(random_bytes(64)), PHP_EOL;' - 2.
base64 でエンコードして保存し、デコードしたバイト列を使う
起動時に一度だけ長さを検証してください。そうすれば、弱いシークレットは最初のリクエストではなくデプロイの段階で失敗します。
php $secret = base64_decode(getenv('JWT_SECRET_B64'), true); if ($secret === false || strlen($secret) < 32) { throw new RuntimeException('JWT_SECRET_B64 must decode to at least 32 bytes'); } $token = JWT::encode($claims, $secret, 'HS256'); $decoded = JWT::decode($token, new Key($secret, 'HS256')); - 3.
短い RSA 鍵を置き換える
2048 ビット以上の RSA 鍵を生成するか、P-256 鍵を使う ES256 に切り替えてください。いずれの場合も、新しい鍵で署名を始める前に、新しい公開鍵を公開しておいてください。
bash openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out rs256-private.pem