JWS 実装時に作りがちな脆弱性パターン

Author: Nov Matake
Date:

JOSE (Javascript Object Signing and Encryption) 愛で満ち溢れる ID 厨界隈において、燦々と輝く JWS (JSON Web Signature)、美しいですよね!

JWT がジャニーズなら、JWE は EXILE、JWS は石原さとみと言ったところでしょうか?

と、冗談はさておき、JWT をお使いの皆さんは、当然署名付けてますよね?署名検証しますよね?

そんなあなたに一言いいたい!

まだ HMAC で消耗してるの?

いや、決して HMAC オワコンとかは言ってないですよ?スマホアプリでの署名検証のために、アプリに共通鍵埋め込むのはナンセンスってだけで。

ということで、今日は JWS をお使いのみなさんに、実装時に作りがちな脆弱性パターンを2つご紹介します。

今日紹介する脆弱性の2つのうち、1つめは HMAC, RSA, ECDSA のどれを使っても対象になるパターン、2つめは公開鍵暗号 (RSA / ECDSA) を使っている場合にのみ対象になるパターンです。

では、さっそく行ってみましょう。

JWS Header の alg を “none” に改ざん

JWS Header には、alg とか typ とか kid とかが入ってるわけですが、この攻撃では alg を “none” に書き換えます。そして、署名部分をごっそり取り除きます。

例えばこんな JWT を

Raw: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQifQ.eyJmb28iOiJiYXIifQ.lmsUbhkgf5KpheRpXwc-pbG_HhYr9Grw1301d0sVzxI
Header: {typ: "JWT", alg: "HS256", kid: "default"}

こんな風に

Raw: eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIiwia2lkIjoiZGVmYXVsdCJ9.eyJmb28iOiJiYXIifQ.
Header: {typ: "JWT", alg: "none", kid: "default"}

これだけで、あとはもう Payload を改ざんし放題。

こういう JWT を受け入れてしまう実装が、意外に多いらしいです。

えぇ、かつて JSON::JWT Gem もそうでした、随分と昔の話ですが。

署名検証する場所では、alg=none な JWT が送られてきたときにちゃんとエラーになるかどうか、確認しましょう。

JWS Header の alg を “HMAC-SHA*” に改ざん

こちらも JWS Header の alg を改ざんするんですが、こちらのケースでは署名はちゃんとついています。

このケースでは、alg を公開鍵暗号方式 (RSA / ECDSA) から共通鍵暗号方式 (HMAC) に差し替え、「公開鍵文字列を共通鍵として利用して」署名を生成します。

前提として、攻撃者が公開鍵を取得できることが必要ですが、まぁ公開鍵は割と誰にでも手に入りそうです。(当社比)

で、今度は Ruby のサンプルコードで説明すると、

private_pem = <<-PEM
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEArUeGng6YrNJT/YbxgWVfJPHtv8tVXNugsuPEt10af+OyCeyk
 :
-----END RSA PRIVATE KEY-----
PEM
private_key = OpenSSL::PKey::RSA.new private_pem

jwt = JSON::JWT.new(foo: :bar)
jws = jwt.sign(private_key, :RS256)

だったのを

public_pem = <<-PEM
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArUeGng6YrNJT/YbxgWVf
 :
-----END PUBLIC KEY-----
PEM

jwt = JSON::JWT.new(malformed: "something attacker added")
jws = jwt.sign(public_pem, :HS256)

にする感じです。

公開鍵をどのようなフォーマットで保存しているかは実装依存でしょうが、その辺は空気読んでいただくとして。

こちらも Payload は改ざんし放題です。

こちらの脆弱性は、JSON::JWT のように公開鍵を鍵オブジェクトとして扱う環境ではエラーになったりしますが、公開鍵を文字列として扱うような環境では以外に署名検証を通過したりしてしまうようです。

例えば PHP とか。

署名検証時に署名アルゴリズムを明示的にチェックするなどの対策が必要でしょう。

まとめ

僕らの JOSE 愛は、こんなことじゃめげないですよ!

でも、気をつけましょうね。

Comments