Blog

2014.12.11

HTTP/2 9.2.2問題

こんにちは、エンジニアの前田です。この記事は、 HTTP2 Advent Calendar 2014 - Qiita の11日目の記事です。

HTTP/2仕様の議論はほぼ終了し、h2-16HPACK-10 が出ました。かんたんなチェックを経てIETF Last Callがかかる予定です。

余談ですが、現在の仕様では “HTTP2.0” ではなく “HTTP/2” もしくは “HTTP2” が正しい名称です。

9.2.2問題(cipher suitesミスマッチ問題)とは?

http-wgメーリングリスト上で、9月から11月にかけて長い議論を呼んだものがあります。 HTTP/2仕様のセクション9.2.2に記述してある部分だったため、「9.2.2問題」と呼ばれています。 2か月間に400通以上のメールがこのトピックに関して流れました。

ドラフト14で、セクション9.2.2に書いてあった部分を引用します(summerwindさんの日本語訳より)

HTTP/2 において許可される TLS 暗号スイートのセットは限定的です。HTTP/2 では、ephemeral Diffie-Hellman (DHE) [TLS12] や elliptic curve variant (ECDHE) [RFC4492] などの ephemeral key exchange に対応する暗号スイートのみが使用されなければなりません (MUST)。ephemeral key exchange は、最低でも2048ビットの DHE か、128ビットのセキュリティレベルの ECDH でなければなりません (MUST)。クライアントは 4096 ビットまでのサイズの DHE を受け入れなければなりません (MUST)。HTTP はストリーム暗号またはブロック暗号を使用する暗号スイートを使用してはなりません (MUST NOT)。AES の Galois Counter Model (GCM) モード [RFC5288] のような Authenticated Encryption with Additional Data (AEAD) モードは受け入れ可能です。

なんかややこしいですが、「TLSには、時代遅れの暗号スイートがある。HTTP/2を使う場合にはそれらを使ってはいけない」ということです。禁止スイートには、RC4など明らかに時代遅れのものが含まれることになります。さらにセクション9.2には、これら禁止スイートを使ってTLS接続した場合、HTTP/2コネクションを INADEQUATE_SECURITY というエラーで終了すべし(MUST)と書かれています。このような要求を果すことで、HTTP/2ではすべての実装の間で安全な(現在安全と考えられている暗号スイートでの)TLS通信を行うことができます。

さて、議論の発端になったのは、OpenJDK7でALPNを実装したJettyサーバと、クライアントFirefox間でHTTP/2コネクションが確立できず、INADEQUATE_SECURITY エラーが発生してしまうという報告でした。これはTLS接続は正しくできたものの、選択された暗号スイートが9.2.2の条件を満たさないダメスイートだったため、クライアントが INADEQUATE_SECURITY で(仕様どおりに)切断した、ということです。

なぜ9.2.2問題が発生するのか

どうしてこんなことになってしまったのでしょう? サーバーのTLSの実装では、cipherなどのハンドシェイクを行う部分とALPNの処理を使う部分は、別のレイヤとして実装しがちです。TLSのハンドシェイクでは、クライアントが使用可能な暗号スイートのリストをサーバーに送り、サーバーがその中からこの接続で使うものを選ぶことになっています。クライアントは、HTTP/2では禁止されているcipherもリストに入れて送ってくるのが普通です(これは相手がHTTP/2に対応していないサーバーだったときにより広い選択肢を与えるためです)。サーバーのcipher層がALPN層の事情を考えず、たまたまこのダメなcipherを選んでしまうと、「HTTP/2で通信したいのにcipherがダメ」現象が発生してしまいます。

ここで3つの対応策がありますが、どれもいまいちです。

  • 案1: cipher層ではHTTP/2で使用可能なcipherが優先して選ばれるように設定しておく
    • クライアントはh2をALPNに入れてくるくらいだから、h2で使えるcipherをリストに入れているはず。だから優先度が高いものが選ばれる。ALPNでh2が指定されていてもいなくても損はしない
  • 案2: cipher層はALPNでh2を選んだ場合は、ダメなcipherを選ばないようにする
  • 案3: cipher層はダメなcipherを選んだ場合は、ALPNではh2を選ばないようにする

案1ではプロトコルの設計の問題を特定の運用設定で解決しようとしています。そのように開き直るべきでしょうか。案2・案3では、cipher層とALPN層が「HTTP/2ではこれこれのcipherは使ってはいけない」という知識を持った上で協調動作しなければなりません。ALPNの仕様(RFC7301)にはこのような協調動作について記述がありませんから、RFC7301だけを見てALPN層を実装すると、9.2.2問題にはまってしまうことになります。

特定のライブラリで協調動作できないからと言って、プロトコルの技術上の問題とは呼べないかもしれません。しかし、広く普及している機能不足によってプロトコル普及が阻害されるならば、これは技術的な問題だと、httpbis WGのChairであるmnotは言っています(9.2.2)。

クライアント側では、もし INADEQUATE_SECURITY を受信したり、自分で INADEQUATE_SECURITY を送信しなければならない状況になった場合何ができるでしょうか? 相手がHTTP/2をしゃべれるのは確定するので、HTTP/2で使える暗号スイートだけをリストに入れてリトライすれば、次はHTTP/2で接続できるでしょう。このためにはTCP接続からやり直さなければならず、その再接続のオーバーヘッドは大きなペナルティーです。せっかくいろいろがんばってTCP接続数を少なくしようとしているHTTP/2なのに、残念な状況になってしまいますね。まあ、最終的にはコストを払って再接続すればいいわけなんですけども、最小限にしたいです。また将来のcipher追加やdeprecationによって、相互運用性が阻害されてしまう心配もあります。

解決策

この問題の解決策はIETF91(ホノルル)で議論されました。以下の6つを行うことが提案され、検討されました。

  1. 暗号スイートの要求をTLS 1.2だけに限定する。TLS 1.3以降の安全なスイート選択はTLS WGにまかせる(おれたちの問題ではないとする)
  2. 暗号スイートの要求を性質で定義するのではなく、具体的な固定のホワイトリストとする(実装が容易になる)
  3. 相互運用性を保証するため、特定の暗号スイートを実装必須とする(TLS 1.2の必須cipherはダメなので、代わりとなる必須cipherが必要)
  4. 9.2.2の条件を満たす責任は実装ではなくデプロイメントであると明記する(設定まで含んでHTTP/2準拠であると言える)
  5. INADEQUATE_SECURITYを返さなければならない条件を緩和する(接続不可能な場合を避けるため、弱いcipherでのHTTP/2通信を許容する)
  6. TLS 1.3以上では TLS_FALLBACK_SCSV サポートを要求する(クライアント・サーバー双方がTLS 1.3以上を実装している場合、再接続でTLS 1.2以下を使うことがないようにする)

これに対し、以下のような意見が出ました。

  • 2番でホワイトリスト方式にすると、新規のcipher suiteが出現したときにその普及が阻害されるおそれがある。ブラックリスト方式のほうがよい
  • ホワイトリスト・ブラックリストのメンテナンスは大変。IANAレジストリを作るのか?
  • ブラックリストが無限に増え続けるのが心配

最終的に、上記の2番と5番を以下のように記述するという解決案で合意に至りました。

  • 固定のブラックリストを仕様に書く。これは現時点のダメなcipherを列挙する
  • h2を選択した際のcipherがブラックリストに入っていたら、INADEQUATE_SECURITY で接続を切ってもよい(MAY)
  • h2を選択した際のcipherがブラックリストに入っていなかったら、INADEQUATE_SECURITY で接続を切ってはいけない(MUST NOT)

これによって、以下のように問題が解決されたことになります。

  • おたがいにHTTP/2をサポートしている場合、少なくとも3番で指定される暗号スイートを使うことができる(ダメ以外の選択肢が少なくともひとつある)
    • cipher選択とALPNによるプロトコル選択が協調していなくてもなんとかなる
  • ブラックリストに入っているcipherをリストに含めない限り、 INADEQUATE_SECURITY をくらう心配はない
  • ダメスイートでのHTTP/2通信は、双方が INADEQUATE_SECURITY を送らないと選んだ、つまり双方がダメスイートのリスクテイクをした場合に限る
  • TLS 1.3が標準化されればTLS 1.3の規定によって解決される
    • 今回はTLS 1.2やALPNの仕様を変えられないことがこの問題の原因のひとつだった
    • TLS 1.3ではHTTP/2のブラックリストに入るようなcipher suiteは除外されるはず

最新のHTTP/2ドラフト h2-16 では、9.2.2はこの合意を反映した記述となっています。そしてAppendix Aにブラックリスト入りした暗号スイートが記述されています。276個もあります! このブラックリストはHTTP/2層に実装すればよく、TLSのcipher層やALPN層に手を入れたくない人でも対応が可能になります。一方で、3番で指定した暗号スイート(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)ではGCMが必要であり、これを実装していない処理系では苦労する場合もあるようです。

そもそもの問題は、HTTP/2 + TLS 1.2でセキュアな通信をしたい一方、HTTP/1.1 + もっと古いTLS/SSLでないとアクセスできないサーバーが世の中にあふれている、という点でもあります。クライアントとしては、古い(設定が最新のベストプラクティスでない)サーバーにアクセスできないのではダメブラウザの烙印を押されてしまいますし、難しい部分なのだと思います。上の方でTLS 1.3になればOK!のように言いましたが、TLS 1.3が十分普及することと、古い設定のサーバーが十分少なくなることとは、必ずしも同じではないです。「HTTP/2になればバラ色!」というのを起爆剤にして、TLSの刷新も進むとよいですね。