The Go Blog

crypto/tlsにおける自動的な暗号スイート順序付け

Filippo Valsorda
2021年9月15日

Go標準ライブラリは、インターネット上で最も重要なセキュリティプロトコルであり、HTTPSの基本要素であるTransport Layer Security (TLS) の堅牢な実装であるcrypto/tlsを提供しています。Go 1.17では、暗号スイートの優先順位付けを自動化することで、その設定をより簡単に、より安全に、より効率的にしました。

暗号スイートの仕組み

暗号スイートは、TLSの先行技術であるSecure Socket Layer (SSL) の時代にまで遡り、「サイファの種類」と呼ばれていました。これらは、TLS_RSA_WITH_AES_256_CBC_SHATLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256といった威圧的な識別子で、TLS接続において鍵交換、証明書の認証、レコードの暗号化に使用されるアルゴリズムを示しています。

暗号スイートはTLSハンドシェイク中にネゴシエートされます。クライアントは、最初のメッセージであるClient Helloでサポートする暗号スイートのリストを送信し、サーバーはそのリストから1つを選択し、その選択をクライアントに伝えます。クライアントはサポートする暗号スイートのリストを自身の優先順位で送信し、サーバーは設定に基づいてクライアントの優先順位またはサーバーの優先順位のいずれかで、相互にサポートされる最初の暗号スイートを選択するのが最も一般的です。

暗号スイートは、実際には多くのネゴシエートされるパラメータの1つに過ぎず、サポートされる曲線/グループや署名アルゴリズムは独自の拡張機能を通じてさらにネゴシエートされますが、最も複雑で有名であり、開発者や管理者が長年にわたって意見を持つように訓練されてきた唯一のものです。

TLS 1.0~1.2では、これらすべてのパラメータが複雑な相互依存関係の網の目で相互作用します。例えば、サポートされる証明書は、サポートされる署名アルゴリズム、サポートされる曲線、およびサポートされる暗号スイートに依存します。TLS 1.3では、これがすべて大幅に簡素化されました。暗号スイートは対称暗号化アルゴリズムのみを指定し、サポートされる曲線/グループが鍵交換を管理し、サポートされる署名アルゴリズムが証明書に適用されます。

開発者に委ねられた複雑な選択

ほとんどのHTTPSおよびTLSサーバーは、暗号スイートと優先順位の選択をサーバーオペレータまたはアプリケーション開発者に委任しています。これは、多くの理由から、最新かつ専門的な知識を必要とする複雑な選択です。

一部の古い暗号スイートには安全でないコンポーネントがあり、一部は安全であるために極めて慎重で複雑な実装を必要とし、一部はクライアントが特定の緩和策を適用するか、特定のハードウェアを持っている場合にのみ安全です。個々のコンポーネントのセキュリティを超えて、異なる暗号スイートは接続全体に劇的に異なるセキュリティ特性を提供できます。ECDHEまたはDHEを使用しない暗号スイートは前方秘匿性を提供しないため、接続は証明書の鍵によって過去に遡ってまたは受動的に復号化されることはありません。最後に、サポートされる暗号スイートの選択は互換性とパフォーマンスに影響を与え、エコシステムの最新の理解なしに変更を行うと、レガシーなクライアントからの接続が切断されたり、サーバーが消費するリソースが増加したり、モバイルクライアントのバッテリーが消耗したりする可能性があります。

この選択は非常に難解でデリケートであるため、優れたMozilla SSL Configuration Generatorのような、オペレータをガイドするための専用ツールが存在します。

なぜこうなったのか、そしてなぜこうなっているのか?

まず、個々の暗号コンポーネントが以前よりもはるかに頻繁に破損していました。2011年、BEAST攻撃がCBC暗号スイートを破り、クライアントのみが攻撃を緩和できるようになったとき、サーバーは影響を受けないRC4を優先するようになりました。2013年、RC4が破られていることが明らかになったとき、サーバーはCBCに戻りました。Lucky Thirteenによって、MAC-then-encrypt設計のためCBC暗号スイートの実装が極めて困難であることが明らかになったとき…他に選択肢はなかったため、実装はCBCを実装するために慎重に困難を乗り越えその困難なタスクで何年も失敗し続けました。設定可能な暗号スイートと暗号アジリティは、コンポーネントが破損したときにその場で置き換えられるという安心感を提供していました。

現代の暗号化は著しく異なります。プロトコルは時折破られることはありますが、個々の抽象化されたコンポーネントが失敗することはほとんどありません。2008年のTLS 1.2から導入されたAEADベースの暗号スイートは、どれも破られていません。最近では、暗号アジリティは負債となっています。これは、弱点やダウングレードにつながる複雑さを導入し、パフォーマンスとコンプライアンスの理由でのみ必要です。

パッチ適用も以前とは異なりました。今日では、開示された脆弱性に対して迅速にソフトウェアパッチを適用することが、安全なソフトウェア展開の基盤であると認識されていますが、10年前は標準的な慣行ではありませんでした。設定変更は、脆弱な暗号スイートに対応するためのより迅速なオプションと見なされ、オペレータは設定を通じてそれらを完全に担当していました。現在ではその逆の問題が発生しています。何年も設定が変更されていないために、完全にパッチが適用され更新されたサーバーが、依然として奇妙に、最適ではなく、または安全でない動作をすることがあります。

最後に、サーバーはクライアントよりも更新が遅れる傾向があり、したがって暗号スイートの最適な選択肢を判断する信頼性が低いと理解されていました。しかし、暗号スイートの選択において最終決定権を持つのはサーバーであるため、強力な意見を持つ代わりに、サーバーがクライアントの優先順位に従うことがデフォルトになりました。これは部分的にはまだ当てはまります。ブラウザは自動更新を実現し、平均的なサーバーよりもはるかに最新です。一方で、多くのレガシーデバイスはサポート対象外となり、古いTLSクライアント設定に固定されているため、最新のサーバーの方が一部のクライアントよりも適切な選択ができることがよくあります。

どのようにしてこうなったかに関わらず、アプリケーション開発者とサーバーオペレーターが暗号スイート選択のニュアンスの専門家となり、設定を最新の状態に保つために最新の開発状況を把握し続けることを要求することは、暗号工学の失敗です。彼らが私たちのセキュリティパッチを適用しているならば、それで十分であるはずです。

Mozilla SSL Configuration Generatorは素晴らしいが、存在すべきではない。

状況は改善されていますか?

ここ数年間の状況の変化には良いニュースと悪いニュースがあります。悪いニュースは、同等のセキュリティ特性を持つ暗号スイートのセットが存在するため、順序付けがさらに微妙になっていることです。そのようなセット内で最良の選択は、利用可能なハードウェアに依存し、設定ファイルで表現するのが困難です。他のシステムでは、当初は単純な暗号スイートのリストだったものが、今ではより複雑な構文SSL_OP_PRIORITIZE_CHACHAのような追加のフラグに依存するようになっています。

良いニュースは、TLS 1.3が暗号スイートを大幅に簡素化し、TLS 1.0~1.2とは異なるセットを使用していることです。すべてのTLS 1.3暗号スイートは安全であるため、アプリケーション開発者やサーバーオペレータはそれらについて心配する必要はまったくありません。実際、BoringSSLやGoのcrypto/tlsのような一部のTLSライブラリでは、それらの設定を一切許可していません。

Goのcrypto/tlsと暗号スイート

Goでは、TLS 1.0~1.2で暗号スイートを設定できます。アプリケーションは常にConfig.CipherSuitesを使用して、有効な暗号スイートと優先順位を設定できました。Config.PreferServerCipherSuitesが設定されていない限り、サーバーはデフォルトでクライアントの優先順位を優先します。

Go 1.12でTLS 1.3を実装した際、TLS 1.3の暗号スイートは設定可能にしませんでした。これは、TLS 1.0–1.2の暗号スイートとは異なるセットであり、最も重要なのはすべて安全であるため、アプリケーションに選択を委ねる必要がないためです。Config.PreferServerCipherSuitesは引き続きどちら側の優先順位が使用されるかを制御し、ローカル側の優先順位は利用可能なハードウェアに依存します。

Go 1.14では、サポートされている暗号スイートを公開しましたが、静的なソート順で優先順位ロジックを表現することに縛られないように、意図的に中立的な順序(ID順)で返すことを選択しました。

Go 1.16では、クライアントまたはサーバーのいずれかがAES-GCMのハードウェアサポートを欠いていることを検出した場合、サーバー側でChaCha20Poly1305暗号スイートをAES-GCMよりも積極的に優先するようになりました。これは、AES-GCMが専用のハードウェアサポート(AES-NIやCLMUL命令セットなど)なしでは効率的かつ安全に実装するのが困難であるためです。

最近リリースされたGo 1.17は、すべてのGoユーザーに対して暗号スイートの優先順位付けを引き継ぎます。Config.CipherSuitesはTLS 1.0-1.2のどの暗号スイートを有効にするかを制御しますが、順序付けには使用されず、Config.PreferServerCipherSuitesは無視されます。代わりに、crypto/tlsは、利用可能な暗号スイート、ローカルハードウェア、および推測されるリモートハードウェアの機能に基づいて、すべての順序付けの決定を行います

現在のTLS 1.0–1.2の順序付けロジックは、以下のルールに従います。

  1. 静的RSA鍵交換よりもECDHEが優先されます。

    暗号スイートの最も重要な特性は、前方秘匿性を有効にすることです。私たちは「古典的」な有限体ディフィー・ヘルマンを実装していません。なぜなら、それは複雑で、遅く、弱く、TLS 1.0–1.2では微妙に壊れているためです。つまり、レガシーな静的RSA鍵交換よりも楕円曲線ディフィー・ヘルマン鍵交換を優先することを意味します。(後者は、接続の秘密を証明書の公開鍵を使用して暗号化するだけで、将来証明書が侵害された場合に復号化することが可能になります。)

  2. 暗号化にはCBCよりもAEADモードが優先されます。

    Lucky13に対する部分的な対策を実装している(2015年のGo標準ライブラリへの最初の貢献!)にもかかわらず、CBCスイートは正しく実装するのが悪夢なので、他の重要なことがすべて同じである場合、代わりにAES-GCMとChaCha20Poly1305を選択します。

  3. 3DES、CBC-SHA256、およびRC4は、他に利用可能なものがない場合にのみ、その優先順位で使用されます。

    3DESは64ビットブロックであり、十分なトラフィックがある場合、誕生日攻撃に対して根本的に脆弱です。3DESはInsecureCipherSuitesにリストされていますが、互換性のためにデフォルトで有効になっています。(優先順位を制御することの追加の利点は、アプリケーションやクライアントが最後の手段として選択すること以外に心配することなく、セキュリティの低い暗号スイートをデフォルトで有効にしておくことができることです。これは、より優れた代替手段をサポートするピアを攻撃するために、より弱い暗号スイートの可用性に依存するダウングレード攻撃がないため安全です。)

    CBC暗号スイートはLucky13スタイルのサイドチャネル攻撃に対して脆弱であり、上記で議論した複雑な対策をSHA-1ハッシュに対しては部分的に実装していますが、SHA-256に対しては実装していません。CBC-SHA1スイートには互換性の価値があり、余分な複雑さを正当化しますが、CBC-SHA256スイートにはそうではないため、デフォルトで無効になっています。

    RC4には、サイドチャネルなしで平文の復元につながる可能性のある実用的に悪用可能な偏りがあります。これ以上悪いことはないので、RC4はデフォルトで無効になっています。

  4. 両側がAES-GCMのハードウェアサポートを持っていない限り、暗号化にはAES-GCMよりもChaCha20Poly1305が優先されます。

    上記で議論したように、AES-GCMはハードウェアサポートなしでは効率的かつ安全に実装するのが困難です。ローカルハードウェアサポートがない場合、または(サーバー側で)クライアントがAES-GCMを優先していないと検出した場合、代わりにChaCha20Poly1305を選択します。

  5. 暗号化にはAES-256よりもAES-128が優先されます。

    AES-256はAES-128よりも大きなキーを持ち、通常は良いことですが、コア暗号化関数のラウンド数も多く、速度が低下します。(AES-256の余分なラウンドはキーサイズの変更とは独立しており、暗号解読に対するより広いマージンを提供しようとする試みです。)より大きなキーは、マルチユーザーおよびポスト量子設定でのみ有用であり、TLSには関連しません。TLSは十分にランダムなIVを生成し、ポスト量子キー交換をサポートしていません。より大きなキーに利点がないため、速度のためにAES-128を優先します。

TLS 1.3の順序付けロジックは、TLS 1.3が最初の3つのルールが防御している問題のあるアルゴリズムを排除したため、最後の2つのルールのみを必要とします。

よくある質問

暗号スイートが破られた場合はどうなりますか?他の脆弱性と同様に、サポートされているすべてのGoバージョンに対してセキュリティリリースで修正されます。すべてのアプリケーションは、安全に動作するためにセキュリティ修正を適用する準備をする必要があります。歴史的に、破られた暗号スイートはますます稀になっています。

なぜTLS 1.0~1.2の暗号スイートは設定可能なままなのですか?どの暗号スイートを有効にするかを選択する際には、ベースラインセキュリティとレガシー互換性の間で意味のあるトレードオフがあり、それはエコシステムの許容できない部分を切り捨てるか、現代のユーザーのセキュリティ保証を低下させるかのどちらかを選択せずに、私たち自身では決定できないことです。

なぜTLS 1.3の暗号スイートは設定可能ではないのですか?逆に、TLS 1.3にはトレードオフがありません。すべての暗号スイートが強力なセキュリティを提供するためです。これにより、開発者の関与なしに、すべてを有効にし、接続の特定の状況に基づいて最速のものを選択できます。

主なポイント

Go 1.17以降、crypto/tlsは利用可能な暗号スイートの選択順序を引き継ぎます。定期的に更新されるGoバージョンを使用することで、潜在的に古いクライアントに順序を選択させるよりも安全であり、パフォーマンスを最適化でき、Go開発者からの重大な複雑さを取り除きます。

これは、暗号化に関する決定を開発者に委ねるのではなく、可能な限り私たちが行うという一般的な哲学、および私たちの暗号化の原則と一致しています。他のTLSライブラリも同様の変更を採用し、デリケートな暗号スイートの設定が過去のものとなることを願っています。

次の記事: 行動規範の更新
前の記事: Goウェブ体験の整理
ブログインデックス