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

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

このコミットは、Go言語のcrypto/tlsパッケージにおけるTLSハンドシェイク処理の改善に関するものです。具体的には、SSLv2ハンドシェイクを受信した場合に、より分かりやすいエラーメッセージを返すように変更されています。

コミット

commit 0a115d72c16961f60a0c2e559b5fab3ca1046993
Author: Adam Langley <agl@golang.org>
Date:   Thu Aug 23 16:44:44 2012 -0400

    crypto/tls: return better error message in the case of an SSLv2 handshake.
    
    Update #3930
    Return a better error message in this situation.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/6474055

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

https://github.com/golang/go/commit/0a115d72c16961f60a0c2e559b5fab3ca1046993

元コミット内容

crypto/tls: return better error message in the case of an SSLv2 handshake.

このコミットは、SSLv2ハンドシェイクの場合に、より良いエラーメッセージを返すようにします。 Issue #3930 を更新します。 この状況でより良いエラーメッセージを返します。

変更の背景

この変更の背景には、Go言語のcrypto/tlsパッケージが、古く、セキュリティ上の脆弱性があるSSLv2プロトコルをサポートしていないという事実があります。しかし、一部の古いクライアントや誤設定されたクライアントが、TLSハンドシェイクの開始時にSSLv2の形式で接続を試みることがあります。

以前のGoのTLS実装では、このようなSSLv2ハンドシェイクを受信した場合、一般的なプロトコルエラーや予期せぬデータとして処理され、ユーザーにとって原因が分かりにくいエラーメッセージが返される可能性がありました。開発者やユーザーが問題の原因を特定しやすくするために、SSLv2ハンドシェイクが検出された場合に、より具体的で分かりやすいエラーメッセージを返すことが求められました。

Issue #3930 は、この問題に対する改善要求であり、このコミットはその要求に応えるものです。セキュリティ上の理由からSSLv2をサポートしないという方針は維持しつつ、互換性の問題やデバッグの困難さを軽減することが目的です。

前提知識の解説

TLS (Transport Layer Security) と SSL (Secure Sockets Layer)

TLSは、インターネット上で安全な通信を行うための暗号化プロトコルです。SSLはTLSの先行バージョンであり、現在ではTLSがその役割を引き継いでいます。TLS/SSLは、クライアントとサーバー間でデータを送受信する際に、盗聴、改ざん、なりすましを防ぐための仕組みを提供します。

TLS/SSLの通信は、以下の主要なフェーズで構成されます。

  1. ハンドシェイクフェーズ: クライアントとサーバーが互いに認証し、暗号化アルゴリズムや鍵をネゴシエートする段階です。このフェーズで、安全な通信路を確立するための準備が行われます。
  2. レコードプロトコルフェーズ: ハンドシェイクフェーズで確立された安全な通信路を使用して、実際のアプリケーションデータを暗号化して送受信する段階です。

SSLv2ハンドシェイク

SSLv2は、SSLプロトコルの初期バージョンであり、現在では多くのセキュリティ上の脆弱性が指摘されています。そのため、現代のTLS実装ではSSLv2のサポートは推奨されておらず、ほとんどの場合無効化されています。

SSLv2のハンドシェイクメッセージは、TLSのハンドシェイクメッセージとは異なるフォーマットを持っています。特に、SSLv2のクライアントハローメッセージは、最初のバイトが0x80で始まることが多く、その後に続く2バイトがメッセージの長さを示すという特徴があります。この0x80という値は、TLSのレコードタイプとしては無効な値です。

TLSレコードプロトコル

TLS通信では、すべてのデータは「レコード」と呼ばれる単位で送受信されます。各レコードは、以下の構造を持っています。

  • Type (1バイト): レコードの種類を示します(例: ハンドシェイク、アプリケーションデータ、アラートなど)。
  • Version (2バイト): プロトコルのバージョンを示します(例: TLS 1.0, TLS 1.2)。
  • Length (2バイト): レコードのペイロードの長さを示します。
  • Payload: 実際のデータ。

TLSのレコードタイプには、recordTypeHandshake (22), recordTypeChangeCipherSpec (20), recordTypeAlert (21), recordTypeApplicationData (23) などがあります。0x80という値は、これらの有効なTLSレコードタイプには含まれません。

alertProtocolVersion

TLSプロトコルにおいて、alertProtocolVersionは、プロトコルバージョンの不一致やサポートされていないバージョンが検出された場合に送信されるアラートメッセージです。このアラートは、通信相手がサポートしていないプロトコルバージョンを使用しようとしたことを示します。

技術的詳細

このコミットは、src/pkg/crypto/tls/conn.goファイル内のreadRecord関数(またはそれに相当するレコード読み込みロジック)に、SSLv2ハンドシェイクを検出するための特定のチェックを追加しています。

TLSレコードの最初のバイトはレコードタイプを示しますが、SSLv2ハンドシェイクの最初のバイトは、TLSの有効なレコードタイプとは異なるパターンを示します。特に、SSLv2のクライアントハローメッセージは、最初のバイトが0x80であることが一般的です。これは、SSLv2のメッセージが、上位ビットがセットされた2バイトの長さフィールドで始まるためです。

コミットによって追加されたロジックは以下の通りです。

  1. 受信したデータの最初のバイト(b.data[0])をtypとして取得します。これは、TLSレコードタイプとして解釈されるべき値です。
  2. 現在のハンドシェイクがrecordTypeHandshakeを期待している状況(want == recordTypeHandshake)で、かつtyp0x80であるかをチェックします。
    • typ == 0x80は、TLSの有効なレコードタイプではないため、通常は不正なデータを示します。
    • しかし、SSLv2ハンドシェイクの特性として、最初のレコードが256バイト未満の長さで、かつMSB(最上位ビット)がセットされたuint16の長さで始まるため、typ0x80になる可能性が高いです。このパターンは、SSLv2クライアントからの接続である可能性を強く示唆します。
  3. もしこの条件が真であれば、それはSSLv2ハンドシェイクであると判断します。
  4. その場合、サーバーはクライアントに対してalertProtocolVersionアラートを送信します。これは、サーバーがクライアントの提示したプロトコルバージョン(SSLv2)をサポートしていないことを明確に伝えます。
  5. そして、errors.New("tls: unsupported SSLv2 handshake received")という、より具体的で分かりやすいエラーメッセージを返します。これにより、デバッグや問題の特定が容易になります。

この変更により、GoのTLSサーバーは、SSLv2クライアントからの接続試行に対して、以前よりも明確なフィードバックを提供するようになります。これは、セキュリティ上の理由からSSLv2をサポートしないという方針を維持しつつ、ユーザーエクスペリエンスとデバッグの容易さを向上させるためのものです。

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

変更はsrc/pkg/crypto/tls/conn.goファイルにあります。

--- a/src/pkg/crypto/tls/conn.go
+++ b/src/pkg/crypto/tls/conn.go
@@ -487,6 +487,16 @@ Again:
 		return err
 	}
 	typ := recordType(b.data[0])
+
+	// No valid TLS record has a type of 0x80, however SSLv2 handshakes
+	// start with a uint16 length where the MSB is set and the first record
+	// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
+	// an SSLv2 client.
+	if want == recordTypeHandshake && typ == 0x80 {
+		c.sendAlert(alertProtocolVersion)
+		return errors.New("tls: unsupported SSLv2 handshake received")
+	}
+
 	vers := uint16(b.data[1])<<8 | uint16(b.data[2])
 	n := int(b.data[3])<<8 | int(b.data[4])
 	if c.haveVers && vers != c.vers {

コアとなるコードの解説

追加されたコードブロックは、readRecord関数(またはそれに相当する部分)内で、TLSレコードの最初のバイトを読み取った直後に実行されます。

	typ := recordType(b.data[0]) // 受信データの最初のバイトをレコードタイプとして取得

	// No valid TLS record has a type of 0x80, however SSLv2 handshakes
	// start with a uint16 length where the MSB is set and the first record
	// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
	// an SSLv2 client.
	if want == recordTypeHandshake && typ == 0x80 {
		c.sendAlert(alertProtocolVersion) // クライアントにプロトコルバージョンアラートを送信
		return errors.New("tls: unsupported SSLv2 handshake received") // 特定のエラーメッセージを返す
	}
  • typ := recordType(b.data[0]): 受信したバイト列b.dataの最初のバイトをrecordType型にキャストしてtyp変数に格納します。これは、TLSレコードのタイプを識別するためのものです。
  • コメント: 追加されたコメントは、このチェックの意図と背景を明確に説明しています。
    • 「有効なTLSレコードには0x80のタイプは存在しない」という事実。
    • 「SSLv2ハンドシェイクは、MSB(最上位ビット)がセットされたuint16の長さで始まり、最初のレコードは常に256バイト未満である」というSSLv2の特性。
    • これらの理由から、「typ == 0x80はSSLv2クライアントからの接続である可能性を強く示唆する」という結論。
  • if want == recordTypeHandshake && typ == 0x80:
    • want == recordTypeHandshake: 現在、ハンドシェイクメッセージを期待している状態であることを確認します。これは、TLS接続の初期段階でこのチェックが意味を持つことを保証します。
    • typ == 0x80: 受信したレコードの最初のバイトが0x80であるかをチェックします。
  • c.sendAlert(alertProtocolVersion): 条件が満たされた場合、alertProtocolVersionというアラートメッセージをクライアントに送信します。これにより、クライアントはサーバーがそのプロトコルバージョン(SSLv2)をサポートしていないことを認識できます。
  • return errors.New("tls: unsupported SSLv2 handshake received"): 最後に、"tls: unsupported SSLv2 handshake received"という具体的なエラーメッセージを含むエラーを返します。これにより、アプリケーションレベルでこの特定のエラーを捕捉し、適切な処理を行うことが可能になります。

このコードは、TLSプロトコルの厳密な解釈と、SSLv2の既知の特性を組み合わせることで、より堅牢でユーザーフレンドリーなエラーハンドリングを実現しています。

関連リンク

参考にした情報源リンク

  • RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 (特にレコードプロトコルとハンドシェイクプロトコルに関するセクション)
  • RFC 6101: The Secure Sockets Layer (SSL) Protocol Version 3.0 (SSLv2の特性を理解するための背景情報)
  • TLS/SSLのハンドシェイクプロセスに関する一般的な技術記事やドキュメント
  • Go言語のcrypto/tlsパッケージのソースコード
  • SSLv2クライアントハローメッセージのフォーマットに関する情報