JOSE(JavaScriptオブジェクトへの署名と暗号化)は、絶対に避けるべき悪い標準規格である

注: 本稿は元はJSON Web Tokens(JWT)について書いたものですが、JWTはJavascript Object Signing and Encryption(JOSE)のサブセットであるため、以下の批評はどちらかというとJOSE全体に焦点を当てています。

もし既にJavascript Object Signing and Encryption(JOSE)を実装することを決めているなら、それがJSON Web Tokens、JSON Web Encryption(JWE)、JSON Web Signatures(JWS)のいずれであっても、その決断に疑問を持つべきです。間違いを犯そうとしている可能性があります。

この投稿に書いたことはすべて、RFC 7519、RFC 7515、そしてRFC 7516に則っています。将来、新規のRFCでは以下に挙げるような欠陥はなくなっている可能性はあります。

暗号化の基本的な用語と概念について知る必要がある場合は、先にこの記事を読んでください。記事全体を通して、詳細を補足し、本文を簡潔にするため、適宜外部リンクを張っています。

Javascript Object Signing and Encryptionを使うべきでない理由

JOSE標準規格群には複数の問題があります。それは特定の実装に限定された欠陥ではなく、現実に多数のライブラリがこの欠損を抱えた標準規格の上で動いています。

JSON Web Tokensはしばしば誤った使い方をされている

多くの開発者たちが、セッションのサーバサイド保存を避けるためにJWTを用いようとします。これがほぼ例外なく大きな間違いであり、開発者に、注意深いエンジニアリングではなく、小賢しい言い訳とその場しのぎの対策をさせる原因になります。

上記でリンクした2つの記事は、それが悪手である理由を簡潔に説明していますので、システムアーキテクチャの問題についてここで詳細は述べません。それよりも、より切迫した問題があります。標準規格そのものが不良品であり、セキュアでない状態を誘導してしまうのです。

JSON Web Signaturesが偽造を容易にする

JSON Web Signatures (JWS)はメッセージ認証デジタル署名を提供する標準規格です。

一般的に言えば、暗号化プロトコルにデジタル署名やメッセージ認証が含まれている場合、攻撃者が無作為のメッセージの偽造に低コストで成功すると、プロトコルが完全に壊れていると考えられます。

RFC 7515、 セクション 4.1.1の引用:

“alg”(アルゴリズム) ヘッダパラメータは、JWSをセキュアにするために使う暗号アルゴリズムを識別します。JWS Signature値は、”alg”値がサポートされたアルゴリズムを表していない場合、または、デジタル署名、保護コンテンツのパーティと連携するアルゴリズムと共用するキーがない場合は、認められません。”alg”値は、[JWA]が定めたIANA “JSON Web Signature and Encryption Algorithms”レジストリに登録するか、Collision-Resistant Nameを含む値にする必要があります。”alg”値は、StringOrURI値を含むケースセンシティブなASCII文字列です。このヘッダパラメータは必ず提示し、実行時に解釈、処理されなければなりません。

以前これが、ほとんどのJWTライブラリにおいて重大な脆弱性という形で、JWSユーザに危害を与えたのを見てきました。

標準準拠のJWSライブラリを攻撃し、トークン偽造を達成するには2つの方法がありました。

  1. “none”アルゴリズムを識別するヘッダを送信する
  2. アプリケーションが正常にRSA公開鍵でメッセージにサインする時、”HS256″アルゴリズムを識別するヘッダを送信する

これは単なる実装のバグではなく、欠陥を持った標準規格の結果です。セキュリティのためにそのような標準規格を信頼すべきではありません。標準規格を忠実に守ると、ヘッダを処理し、”解釈”しなければならないことになります。攻撃者が書き込んだヘッダを破棄することさえ、標準によって明示的に禁止されているのです。

“解釈と処理”は何を意味するのか

JSONとJOSE RFCで何度も繰り返されるフレーズですが、その正式な定義は示されていません。

現在、ライブラリの多くは”ホワイトリストに照らしてalgヘッダを承認”することを、”処理済みである”という条件を満たしたと解釈します。algヘッダの変更によって起きる以前のトークン偽造の脆弱性のことを考慮すると、それが常に当てはまるようには見えません。

実装のセキュリティを高めるためにセキュリティプロトコルの曖昧さを活用した開発者を賞賛したくもなりますが、それで標準規格を修正できるわけではありません。

JSON Web Encryptionはフットガンである

暗号化には、実行エラーの起こる余地がたくさん残っています。特に非対称(いわゆる公開鍵)の暗号化が関わる場合はその可能性が高いです。

JWEの認める暗号化アルゴリズムはRFC 7518の2つのセクションに記載されています。

  • 鍵暗号化:選択肢
    • PKCS #1v1.5パティング付きRSA
    • OAEPパディング付きRSA
    • ECDH
    • AES-GCM
  • メッセージ暗号化:次のいずれかを選択
    • AES-CBC + HMAC
    • AES-GCM

鍵暗号の選択肢について少し考えましょう。メッセージ暗号については考えなくても大丈夫です(GCMの実装が確実で、適切なハードウェアサポートがあるとすれば)。

暗号化技術をよく知らない人は、鍵暗号の選択とは、弾倉の6室中5室に弾が装填された銃を足に向けて、弾が当たらないと期待するようなものだと思ってください。どれを選んだとしても、多かれ少なかれセキュリティの問題はあります。

PKCS #1v1.5パティング付きRSA

PKCS #1v1.5パディング付きRSAは、パディングオラクルと呼ばれる一種の選択暗号文攻撃に対して脆弱です。RSA攻撃についての議論がここにあります。

OAEPパディング付きRSA

OAEPパディング付きRSAは、おそらくセキュアです。OAEPのセキュリティ証明は偽ですが、RSA自体がセキュアだと仮定すれば、他の点ではセキュアです。しかし、RSAの長期的なセキュリティには深刻な疑念があります。

ほとんどの暗号専門家は、RSAから移行するように勧めています。

ECDH

JWTでは、NIST曲線(ワイエルシュトラス曲線。攻撃者が秘密鍵を盗むinvalid-curve攻撃の危険がある)の1つについて楕円曲線ディフィー・ヘルマン(ECDH)鍵共有のみが許可されます。

セキュリティのための楕円曲線の1つを使ってinvalid curve攻撃の回避を試みるとしたら、それはすでにJWT標準に準拠していません。

AES-GCM

疑わしい公開鍵暗号モードのリストは、共有鍵暗号モードを使用しないと完成できないので、JOSE標準規格では、AES-GCM鍵を交換するためにAES-GCMの使用が許可されます。


暗号化アルゴリズムの選択は、開発者ではなく、暗号の専門家によって行われるべきです。開発者に任せておくとプロトコルとアルゴリズムのミスマッチを招き、間違いなく、エラーを起こしやすい暗号化設計ができあがるでしょう。

余談:このセクションで触れているJOSEでの最近のinvalid curve攻撃は、まだお読みになっていないなら、一読の価値があります。

JOSEより優れた解決策

JOSEより優れた標準規格では、選ぶべきパラメータは2つだけです。

  • ver – プロトコルのどのバージョンを使用するか。それにより、各バージョンに対して、”ただ1つの真の暗号スイート”が定まります。以下は単なる例です。
    • v1:Ed25519、X25519、XSalsa20poly1305、 HMAC-SHA-512-256
    • v2:Ed448、X448、XChaCha20Poly1305、鍵付きBLAKE2b
    • v3:SPHINCS-256、SIDH、NORX64-4-1、鍵付きBLAKE2x
  • op – どのような操作を行うか。
    • 認証付き暗号化
    • メッセージ認証
    • 公開鍵の認証付き暗号化(サーバ間通信向け)
    • 公開鍵のデジタル署名(サーバ間通信向け)

可能なら、公開鍵の暗号化は避けるべきです。

よりよい標準規格に最も近いのは、Fernetです。

まとめ

以上をまとめると、

  • 他の記事で述べられているように、セッション管理にJWTを使ってはならない。
  • JWS標準規格は完全に壊れているので、RFCに準拠すると、アプリケーションが脆弱になる。
  • JWE標準規格は地雷原なので、暗号の専門家以外を歩き回らせてはならない。
  • JOSEはセキュリティの欠陥が染みついた、複雑な寄せ集めの標準規格群である。

JOSE / JWT / JWE / JWSの代わりに何を使うべきか?

前述したように、ほとんどのJWT適用例では、Fernetが1つの選択肢になります。次のように言われています。

  • セキュアなセッション:単にHTTPSにクッキーを使用する。クッキーは、サーバ側の恒久的ストレージ媒体とペアにされる、ランダムな識別子のみを記憶すべき。
  • 署名:Libsodiumのcrypto_sign()またはcrypto_auth()API(ユースケースに応じて)。
  • 暗号:Libsodiumのcrypto_secretbox()またはcrypto_box()API(ユースケースに応じて)。

私たちは既に、JSON Webトークンの必要性を完全になくす、トークンベースのセキュアな認証クロスドメインユーザ認証に関する資料を公表しています。

あなたのチームは2015年にJWTライブラリを監査したのに、今になってJOSEはよくないと言うのか?

確かに、私たちは2015年にJWTライブラリの無料監査を行い、合格としました(ただし、例えばPHPECCの不使用などの、いくつかの警告付きでした)。JOSEがよくないからといって、単純にすべての実装やライブラリがセキュアでないとは言えません。

しかし、監査が行われた後にライブラリに次の変更が加えられたことには留意すべきです。

この種の危険な決定は、脆弱性が起こりうる可能性を高めるものでしかありません。なぜなら、この標準規格はエラーを呼ぶ暗号設計であり、実装者とユーザにかかる負担が大きすぎるからです。より良い標準規格が必要なのです。

結論:JOSE / JWT / JWS / JWE ダメ、絶対。