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

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

このコミットは、Go言語の crypto/tls パッケージにおける close_notify アラートの扱いを変更するものです。具体的には、このアラートのレベルを「致命的 (fatal)」から「警告 (warning)」へと変更しています。これにより、TLSセッション終了時の挙動が、より一般的な実装に沿うようになります。

コミット

commit cfa1ba34cc98c5f804d5ab9672b09acf43fee102
Author: Adam Langley <agl@golang.org>
Date:   Tue Oct 16 15:40:37 2012 -0400

    crypto/tls: make closeNotify a warning alert.
    
    The RFC doesn't actually have an opinion on whether this is a fatal or
    warning level alert, but common practice suggests that it should be a
    warning.
    
    This involves rebasing most of the tests.
    
    Fixes #3413.
    
    R=golang-dev, shanemhansen, rsc
    CC=golang-dev
    https://golang.org/cl/6654050

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

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

元コミット内容

crypto/tls: make closeNotify a warning alert.

The RFC doesn't actually have an opinion on whether this is a fatal or
warning level alert, but common practice suggests that it should be a
warning.

This involves rebasing most of the tests.

Fixes #3413.

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

変更の背景

TLS (Transport Layer Security) プロトコルにおいて、セッションの正常な終了を示すために close_notify アラートが使用されます。このアラートは、通信の相手側が接続を正常に終了しようとしていることを通知するものです。

このコミットが行われる前、Go言語の crypto/tls パッケージでは close_notify アラートを「致命的 (fatal)」なアラートとして扱っていました。致命的なアラートを受信すると、TLS接続は即座に終了し、エラーとして扱われます。しかし、TLSのRFC (Request for Comments) は close_notify アラートが致命的であるべきか、警告であるべきかについて明確な規定を持っていませんでした。

一方で、多くの既存のTLS実装や一般的な慣行では、close_notify を「警告 (warning)」レベルのアラートとして扱っていました。警告レベルのアラートは、接続を即座に終了させることなく、情報として処理されることが期待されます。Go言語の実装が close_notify を致命的として扱うことで、他のTLS実装との相互運用性において問題が発生する可能性がありました。例えば、他のシステムが正常に close_notify を送信して接続を閉じようとした際に、Goのクライアントやサーバーがそれをエラーとして処理し、予期せぬ接続切断やエラーログの増加を引き起こす可能性がありました。

このコミットは、このような相互運用性の問題を解消し、GoのTLS実装をより一般的な慣行に合わせることを目的としています。これにより、TLS接続の正常な終了がスムーズに行われるようになり、不必要なエラー処理が回避されます。

前提知識の解説

TLS (Transport Layer Security)

TLSは、インターネット上での通信を暗号化し、データの完全性と認証を提供するプロトコルです。ウェブブラウジング(HTTPS)、電子メール(SMTPS)、VoIPなど、様々なアプリケーションで利用されています。TLSは、クライアントとサーバー間で安全な通信チャネルを確立するために、ハンドシェイクプロトコル、レコードプロトコル、アラートプロトコルなど、複数のサブプロトコルで構成されています。

TLSアラートプロトコル

TLSアラートプロトコルは、TLS接続中に発生したエラーや状態変化を相手に通知するために使用されます。アラートメッセージは、AlertLevelAlertDescription の2つのバイトで構成されます。

  • AlertLevel: アラートの深刻度を示します。

    • warning (1): 警告レベルのアラート。通常、接続を即座に終了させる必要はありませんが、何らかの注意が必要な状態を示します。
    • fatal (2): 致命的レベルのアラート。接続を即座に終了させる必要がある深刻なエラーを示します。
  • AlertDescription: 発生した具体的なアラートの種類を示します。例えば、close_notify (0) は正常な接続終了、unexpected_message (10) は予期せぬメッセージ、bad_record_mac (20) はMAC検証失敗などがあります。

close_notify アラート

close_notify (アラート記述子 0) は、TLS接続の終了を相手に通知するために送信されるアラートです。これは、アプリケーションデータがこれ以上送信されないことを示し、接続を正常にシャットダウンするプロセスを開始します。理想的には、TLS接続は両端が close_notify を送信し、受信することで、きれいに終了します。

致命的アラートと警告アラートの挙動の違い

  • 致命的アラート (Fatal Alert): 致命的アラートを受信した場合、TLS実装は直ちに接続を終了し、それ以上のデータ送受信を停止します。これは、プロトコル違反、暗号化エラー、認証失敗など、回復不可能なエラーが発生した場合に用いられます。
  • 警告アラート (Warning Alert): 警告アラートを受信した場合、TLS実装は通常、接続を継続します。警告は、情報提供や、接続の継続に影響を与えない軽微な問題を示すために使用されます。例えば、close_notify が警告として扱われる場合、相手が接続を閉じようとしていることを知っても、すぐに接続を切断するのではなく、残りのデータを処理したり、自身も close_notify を送信して正常に終了したりする機会が得られます。

技術的詳細

このコミットの技術的な変更点は、主に crypto/tls パッケージ内のアラート送信ロジックと、それに関連するテストケースの修正にあります。

TLS接続においてアラートメッセージを送信する際、そのアラートが警告レベルであるか致命的レベルであるかを決定する必要があります。以前のGoの実装では、close_notify アラートは alertLevelError (致命的レベル) として扱われていました。

このコミットでは、src/pkg/crypto/tls/conn.go 内の sendAlertLocked 関数が修正され、close_notify アラートが alertLevelWarning (警告レベル) として送信されるように変更されました。これにより、GoのTLS実装が close_notify を受信した際に、他の一般的なTLS実装と同様に、接続を即座に切断するのではなく、警告として処理するようになります。

この変更に伴い、src/pkg/crypto/tls/handshake_client_test.gosrc/pkg/crypto/tls/handshake_server_test.go のテストコードが大幅に修正されています。これは、テストケース内で期待されるTLSハンドシェイクのバイトシーケンスやアラートの挙動が変更されたためです。特に、close_notify が警告として扱われるようになったことで、テストシナリオにおける接続終了時の期待される挙動が変わり、それに合わせてテストデータ(バイト列)を「リベース」する必要がありました。これは、テストがTLSプロトコルの詳細なバイトレベルの挙動を検証しているため、小さなプロトコル変更でも広範囲なテストデータの更新が必要となることを示しています。

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

変更されたファイルは以下の3つです。

  1. src/pkg/crypto/tls/conn.go: TLS接続のアラート送信ロジックが変更されました。
  2. src/pkg/crypto/tls/handshake_client_test.go: クライアント側のTLSハンドシェイクテストが、close_notify の挙動変更に合わせて更新されました。
  3. src/pkg/crypto/tls/handshake_server_test.go: サーバー側のTLSハンドシェイクテストが、close_notify の挙動変更に合わせて更新されました。

特に重要な変更は src/pkg/crypto/tls/conn.go にあります。

--- a/src/pkg/crypto/tls/conn.go
+++ b/src/pkg/crypto/tls/conn.go
@@ -604,9 +604,11 @@ Again:
 // sendAlert sends a TLS alert message.
 // c.out.Mutex <= L.
 func (c *Conn) sendAlertLocked(err alert) error {
-	c.tmp[0] = alertLevelError
-	if err == alertNoRenegotiation {
+	switch err {
+	case alertNoRenegotiation, alertCloseNotify:
 		c.tmp[0] = alertLevelWarning
+	default:
+		c.tmp[0] = alertLevelError
 	}
 	c.tmp[1] = byte(err)
 	c.writeRecord(recordTypeAlert, c.tmp[0:2])

コアとなるコードの解説

上記の差分は、Conn 構造体の sendAlertLocked メソッドにおける変更を示しています。このメソッドは、TLSアラートメッセージを送信する際に、アラートのレベル(警告または致命的)を設定する役割を担っています。

変更前は、以下のロジックでした。

func (c *Conn) sendAlertLocked(err alert) error {
	c.tmp[0] = alertLevelError // デフォルトで致命的エラーを設定
	if err == alertNoRenegotiation { // alertNoRenegotiation の場合のみ警告
		c.tmp[0] = alertLevelWarning
	}
	// ...
}

このコードでは、alertNoRenegotiation 以外のアラートはすべてデフォルトで alertLevelError (致命的) として扱われていました。したがって、alertCloseNotify も致命的アラートとして送信されていました。

変更後は、switch ステートメントが導入されました。

func (c *Conn) sendAlertLocked(err alert) error {
	switch err {
	case alertNoRenegotiation, alertCloseNotify: // alertNoRenegotiation または alertCloseNotify の場合
		c.tmp[0] = alertLevelWarning // 警告レベルを設定
	default: // それ以外のアラートの場合
		c.tmp[0] = alertLevelError // 致命的レベルを設定
	}
	c.tmp[1] = byte(err)
	c.writeRecord(recordTypeAlert, c.tmp[0:2])
	return nil
}

この新しいロジックでは、alertNoRenegotiationalertCloseNotify の両方が明示的に alertLevelWarning として扱われるようになりました。その他のアラートは引き続き alertLevelError として扱われます。

この変更により、GoのTLS実装が close_notify アラートを送信する際に、そのレベルが警告として設定されるようになり、受信側がそれを致命的なエラーとして処理する可能性が低減されます。これは、TLS接続の正常な終了プロセスをより堅牢にし、異なるTLS実装間での相互運用性を向上させる上で重要な変更です。

テストファイルの変更は、このプロトコルレベルの挙動変更に伴い、期待されるバイトシーケンスが変化したため、テストデータ(特にTLSレコードのヘッダ部分に含まれるアラートレベルのバイト)を更新したものです。

関連リンク

参考にした情報源リンク