Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11512] ファイルの概要

このコミットは、Go言語の crypto/tls パッケージにおけるクライアント側のハンドシェイク処理に関する改善です。具体的には、SSLv3サーバーへの接続時に発生するエラーメッセージをより適切にするための変更が含まれています。GoのTLSクライアントはSSLv3をサポートしないため、この変更は、サポートされていないプロトコルバージョンで接続しようとした際のエラーハンドリングを改善することを目的としています。

コミット

commit c86e03975ce6fd0fcf7dcf35e8110057234170ab
Author: Adam Langley <agl@golang.org>
Date:   Tue Jan 31 11:22:47 2012 -0500

    crypto/tls: better error message when connecting to SSLv3 servers.
    
    We support SSLv3 as a server but not as a client (and we don't want to
    support it as a client). This change fixes the error message when
    connecting to an SSLv3 server since SSLv3 support on the server side
    made mutualVersion accept SSLv3.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5545073

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/c86e03975ce6fd0fcf7dcf35e8110057234170ab

元コミット内容

crypto/tls: better error message when connecting to SSLv3 servers.

We support SSLv3 as a server but not as a client (and we don't want to support it as a client). This change fixes the error message when connecting to an SSLv3 server since SSLv3 support on the server side made mutualVersion accept SSLv3.

R=golang-dev, rsc CC=golang-dev https://golang.org/cl/5545073

変更の背景

この変更が行われた2012年当時、SSLv3はまだ広く利用されていましたが、既にセキュリティ上の脆弱性が指摘され始めていました。Go言語の crypto/tls パッケージは、設計思想としてよりセキュアなプロトコルを推奨し、古い、または脆弱なプロトコルからの移行を促す傾向にありました。

コミットメッセージによると、GoのTLS実装はサーバーとしてはSSLv3をサポートしていましたが、クライアントとしてはサポートしていませんでした。これは、クライアントがより新しい、よりセキュアなプロトコル(TLS 1.0以降)を使用することを強制することで、通信の安全性を高めるという意図があったと考えられます。

しかし、mutualVersion 関数(クライアントとサーバーが共通してサポートするTLS/SSLバージョンを決定する関数)が、サーバー側でのSSLv3サポートのためにSSLv3を受け入れてしまうという問題がありました。このため、クライアントがSSLv3サーバーに接続しようとした際に、期待されるエラーメッセージではなく、異なる、あるいは不明瞭なエラーが発生する可能性がありました。

このコミットは、クライアントがSSLv3サーバーに接続しようとした際に、より明確なエラーメッセージ(alertProtocolVersion)を返すようにすることで、ユーザーが問題の原因を特定しやすくすることを目的としています。これは、GoのTLSクライアントがSSLv3をサポートしないという設計意図を、エラーハンドリングの面からも明確にするための修正です。

前提知識の解説

SSL/TLSプロトコル

SSL (Secure Sockets Layer) と TLS (Transport Layer Security) は、インターネット上でデータを安全にやり取りするための暗号化プロトコルです。SSLはTLSの前身であり、SSL 3.0の後にTLS 1.0が発表されました。

  • SSLv3 (Secure Sockets Layer version 3.0): 1996年にNetscapeによって開発されたプロトコルです。当時としては画期的なセキュリティ機能を提供しましたが、後にPOODLE (Padding Oracle On Downgraded Legacy Encryption) などの重大な脆弱性が発見され、現在では非推奨とされています。
  • TLS (Transport Layer Security): SSLv3の後継としてIETFによって標準化されました。TLS 1.0、1.1、1.2、1.3とバージョンアップを重ね、セキュリティが強化されています。現在ではTLS 1.2およびTLS 1.3が広く利用されており、TLS 1.0と1.1も非推奨となっています。

クライアントとサーバーの役割

TLS/SSL通信では、クライアントとサーバーがハンドシェイクと呼ばれる一連のプロセスを通じて、安全な通信チャネルを確立します。

  • クライアント: 通信を開始する側。ウェブブラウザやメールクライアントなどが該当します。
  • サーバー: クライアントからの接続要求を受け入れる側。ウェブサーバーやメールサーバーなどが該当します。

ハンドシェイクの過程で、クライアントとサーバーは互いにサポートするプロトコルバージョンや暗号スイート(暗号化アルゴリズムの組み合わせ)を交換し、共通して利用可能な最もセキュアなものを選択します。

Go言語の crypto/tls パッケージ

Go言語の標準ライブラリには、TLS/SSLプロトコルを実装した crypto/tls パッケージが含まれています。このパッケージは、Goアプリケーションでセキュアなネットワーク通信を容易に実現するために提供されています。Goの設計思想として、セキュリティを重視し、デフォルトで安全な設定を提供する傾向があります。

mutualVersion 関数

mutualVersion 関数は、TLSハンドシェイクの過程で、クライアントとサーバーが共通してサポートするTLS/SSLプロトコルバージョンを決定するために使用されます。この関数は、両者が提示するバージョンの中から、最も新しい(セキュアな)バージョンを選択しようとします。

versionTLS10 定数

versionTLS10 は、Goの crypto/tls パッケージ内で定義されている定数で、TLS 1.0プロトコルバージョンを表します。この定数は、サポートされる最小のTLSバージョンをチェックする際に使用されます。

技術的詳細

このコミットの核心は、crypto/tls パッケージ内のクライアントハンドシェイクロジック、具体的には clientHandshake() 関数におけるプロトコルバージョンのチェック方法の変更にあります。

変更前は、mutualVersion 関数がクライアントとサーバー間で合意されたバージョンを返していました。もし mutualVersion が有効なバージョンを返さなかった場合(!ok)、エラーとして alertProtocolVersion を返していました。

しかし、コミットメッセージにあるように、サーバー側でSSLv3をサポートしている場合、mutualVersion はSSLv3を有効なバージョンとして受け入れてしまう可能性がありました。これは、mutualVersion が単に共通のバージョンを見つけるだけで、クライアントがサポートする最小バージョンを考慮していなかったためです。

このコミットでは、この問題を解決するために、mutualVersion が返したバージョンが versionTLS10 (TLS 1.0) よりも古い場合に、明示的にエラーを返す条件を追加しました。

変更された条件は !ok || vers < versionTLS10 です。

  • !ok: mutualVersion が共通のバージョンを見つけられなかった場合。
  • vers < versionTLS10: mutualVersion が共通のバージョンを見つけたが、それがTLS 1.0よりも古いバージョン(つまりSSLv3)だった場合。

この新しい条件により、GoのTLSクライアントは、SSLv3サーバーに接続しようとした際に、たとえ mutualVersion がSSLv3を「共通」と判断したとしても、それを拒否し、alertProtocolVersion エラーを返すようになります。これにより、GoクライアントがSSLv3をサポートしないという意図が、エラーメッセージを通じて明確に伝わるようになりました。

alertProtocolVersion は、TLSプロトコルにおいて、相手がサポートしていないプロトコルバージョンを提示した場合に送信されるアラートです。このアラートを返すことで、クライアントはサーバーに対して、より新しいプロトコルバージョンを使用する必要があることを効果的に伝えます。

コアとなるコードの変更箇所

変更は src/pkg/crypto/tls/handshake_client.go ファイルの以下の部分です。

--- a/src/pkg/crypto/tls/handshake_client.go
+++ b/src/pkg/crypto/tls/handshake_client.go
@@ -59,7 +59,8 @@ func (c *Conn) clientHandshake() error {
 	finishedHash.Write(serverHello.marshal())
 
 	vers, ok := mutualVersion(serverHello.vers)
-	if !ok {
+	if !ok || vers < versionTLS10 {
+		// TLS 1.0 is the minimum version supported as a client.
 		return c.sendAlert(alertProtocolVersion)
 	}
 	c.vers = vers

コアとなるコードの解説

変更されたコードは clientHandshake() 関数内にあります。この関数は、TLSクライアントがサーバーとのハンドシェイクを行う主要なロジックを含んでいます。

  1. vers, ok := mutualVersion(serverHello.vers):

    • serverHello.vers は、サーバーが提示してきたプロトコルバージョンです。
    • mutualVersion 関数は、クライアントがサポートするバージョンとサーバーが提示したバージョンを比較し、共通して利用可能な最も新しいバージョンを vers に、そしてそのバージョンが見つかったかどうかを ok に返します。
  2. if !ok || vers < versionTLS10 { ... }:

    • これが変更された条件文です。
    • !ok: mutualVersion が共通のバージョンを見つけられなかった場合(例: サーバーがクライアントが全くサポートしない非常に古いバージョンを提示した場合)。
    • vers < versionTLS10: mutualVersion が共通のバージョンを見つけたが、それが versionTLS10(TLS 1.0)よりも古いバージョンだった場合。この場合、実質的にSSLv3が該当します。
    • この条件が真の場合、つまり共通のバージョンが見つからないか、見つかってもTLS 1.0より古い場合は、以下の処理が実行されます。
  3. // TLS 1.0 is the minimum version supported as a client.:

    • 追加されたコメントです。GoのTLSクライアントがTLS 1.0を最小サポートバージョンとしていることを明確に示しています。
  4. return c.sendAlert(alertProtocolVersion):

    • alertProtocolVersion は、TLSプロトコルで定義されているアラートメッセージの一つで、プロトコルバージョンの不一致を示します。
    • このアラートをサーバーに送信し、ハンドシェイクをエラーとして終了させます。これにより、クライアントはSSLv3サーバーとの接続を拒否し、その理由を明確に伝えることができます。

この変更により、GoのTLSクライアントは、SSLv3サーバーとの接続試行に対して、より正確で分かりやすいエラーメッセージを返すようになり、デバッグや問題解決が容易になりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメントおよびソースコード
  • TLS/SSLプロトコルに関するRFCドキュメント
  • TLS/SSLのバージョンと脆弱性に関する一般的なセキュリティ情報源
  • コミットメッセージに記載されているGoのコードレビューシステム (Gerrit) のリンク: https://golang.org/cl/5545073 (現在はGoのGerritインスタンスは閉鎖されており、GitHubに移行しています。このリンクは直接アクセスできませんが、コミットメッセージの文脈を理解する上で参考になります。)
  • POODLE攻撃に関する情報: https://ja.wikipedia.org/wiki/POODLE%E6%94%BB%E6%92%83 (このコミットの時点ではPOODLEはまだ発見されていませんが、SSLv3が非推奨となる背景を理解する上で重要です。)
  • GoのTLSクライアントがSSLv3をサポートしない理由に関する議論 (当時のメーリングリストなど):