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

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

このコミットは、Go言語の標準ライブラリ crypto/tls パッケージにおけるドキュメンテーションの改善に関するものです。特に、TLSクライアント接続を確立する際に使用される Config 構造体の ServerName フィールドと、Client 関数に関する説明が更新されています。この変更の目的は、ユーザーがTLS証明書の検証メカニズムをより正確に理解し、安全な接続を確立できるようにすることにあります。

コミット

commit 80692a3f81f6367e9c61b652bf3dff30f4cc6624
Author: Adam Langley <agl@golang.org>
Date:   Wed Feb 19 11:17:09 2014 -0500

    crypto/tls: improve documentation for ServerName.
    
    Users of the low-level, Client function are frequenctly missing the
    fact that, unless they pass a ServerName to the TLS connection then it
    cannot verify the certificates against any name.
    
    This change makes it clear that at least one of InsecureSkipVerify and
    ServerName should always be set.
    
    LGTM=bradfitz
    R=golang-codereviews, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/65440043

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

https://github.com/golang/go/commit/80692a3f81f6367e9c61b652bf3dff30f4cc6624

元コミット内容

このコミットは、crypto/tls パッケージのドキュメンテーションを改善し、特に ServerName フィールドの重要性を強調することを目的としています。Client 関数を使用する際に、ServerName が設定されていないと証明書の検証が適切に行われないという問題が頻繁に発生していたため、InsecureSkipVerifyServerName のいずれか一方が常に設定されるべきであることを明確にする変更が加えられました。

変更の背景

Go言語の crypto/tls パッケージは、TLS (Transport Layer Security) プロトコルを実装し、セキュアな通信チャネルを提供します。TLSクライアントがサーバーに接続する際、サーバーから提示された証明書を検証することで、通信相手が正当なサーバーであることを確認します。この検証プロセスにおいて、サーバー証明書に記載されたホスト名と、クライアントが接続しようとしているホスト名が一致するかどうかのチェックが重要になります。

しかし、crypto/tls パッケージの低レベルな Client 関数を使用する開発者の中には、Config 構造体の ServerName フィールドの重要性を見落としがちなケースがありました。ServerName フィールドは、TLSハンドシェイク中にServer Name Indication (SNI) 拡張としてサーバーに送信され、仮想ホスティング環境で複数のドメインが同じIPアドレスを共有している場合に、どの証明書を提示すべきかをサーバーに伝える役割を果たします。さらに重要なのは、GoのTLS実装において、この ServerName がサーバー証明書のホスト名検証にも使用される点です。

ServerName が設定されていない場合、GoのTLSクライアントは、提示されたサーバー証明書がどのホスト名に対して発行されたものかを判断できず、結果としてホスト名検証をスキップしてしまう可能性がありました。これは、中間者攻撃 (Man-in-the-Middle attack) のリスクを高め、セキュリティ上の脆弱性につながります。

このコミットは、このような誤用を防ぎ、開発者がより安全なTLS接続を構築できるように、既存のドキュメンテーションをより明確にすることを目的としています。特に、InsecureSkipVerify (証明書検証を完全にスキップする設定) を使用しない限り、ServerName を必ず設定する必要があるという点を強調しています。

前提知識の解説

TLS (Transport Layer Security)

TLSは、インターネット上で安全な通信を行うための暗号化プロトコルです。主に以下の機能を提供します。

  • 認証: 通信相手が正当なサーバー(またはクライアント)であることを確認します。サーバー認証にはX.509デジタル証明書が使用されます。
  • 機密性: 通信内容が第三者に盗聴されないように暗号化します。
  • 完全性: 通信内容が途中で改ざんされていないことを保証します。

TLSハンドシェイクは、クライアントとサーバー間で暗号化パラメータをネゴシエートし、セキュアな通信チャネルを確立するプロセスです。

デジタル証明書とホスト名検証

デジタル証明書は、公開鍵と、その公開鍵が特定のエンティティ(例: ウェブサイトのドメイン名)に属することを証明する情報を含んでいます。認証局 (CA) によって署名され、その正当性が保証されます。

TLSクライアントは、サーバーから提示された証明書を検証する際に、以下の点をチェックします。

  1. 証明書が信頼できる認証局によって発行されているか。
  2. 証明書の有効期限が切れていないか。
  3. 証明書に記載されているホスト名(Common NameまたはSubject Alternative Name)が、クライアントが接続しようとしているホスト名と一致するか。

この3番目のチェックが「ホスト名検証」であり、TLS接続のセキュリティにおいて非常に重要です。

Server Name Indication (SNI)

SNIはTLSプロトコルの拡張機能で、クライアントがTLSハンドシェイクの初期段階で接続しようとしているホスト名をサーバーに伝えることを可能にします。これにより、一つのIPアドレスで複数のドメインをホストしているサーバーが、クライアントが要求しているドメインに対応する適切な証明書を提示できるようになります。SNIがない場合、サーバーはどの証明書を提示すべきか判断できず、デフォルトの証明書を提示するか、接続を拒否する可能性があります。

crypto/tls パッケージ

Go言語の crypto/tls パッケージは、TLSプロトコルをGoアプリケーションに統合するための機能を提供します。このパッケージには、TLSクライアントおよびサーバーの機能、証明書の管理、暗号スイートの選択など、TLS通信に必要な多くのコンポーネメントが含まれています。

  • tls.Config 構造体: TLS接続の設定をカプセル化する構造体です。証明書、鍵、ルートCA、サポートするプロトコルバージョン、暗号スイート、そしてこのコミットで焦点となっている ServerNameInsecureSkipVerify などの設定が含まれます。
  • Config.ServerName フィールド: クライアントが接続しようとしているサーバーのホスト名を指定します。この値はSNIとして送信され、サーバー証明書のホスト名検証にも使用されます。
  • Config.InsecureSkipVerify フィールド: このブール値が true に設定されている場合、クライアントはサーバー証明書の検証を完全にスキップします。これは、自己署名証明書を使用するテスト環境など、特定の状況でのみ使用されるべきであり、本番環境での使用はセキュリティリスクを伴うため強く推奨されません。
  • tls.Client(conn net.Conn, config *Config) *Conn 関数: 指定されたネットワーク接続 (net.Conn) とTLS設定 (*tls.Config) を使用して、新しいTLSクライアント接続を確立します。

技術的詳細

このコミットは、既存のGo言語の crypto/tls パッケージ内のドキュメンテーションコメントを修正することで、TLSクライアント接続のセキュリティを向上させることを目的としています。具体的には、tls.Config 構造体の ServerName フィールドと、tls.Client 関数のドキュメンテーションが変更されました。

変更の核心は、ServerName フィールドが単に仮想ホスティングをサポートするためだけでなく、サーバー証明書のホスト名検証にも使用されるという点を明確にすることです。また、InsecureSkipVerifytrue でない限り、この検証が実行されることを強調しています。

さらに、tls.Client 関数のドキュメンテーションでは、config 引数が nil であってはならず、ユーザーは ServerName または InsecureSkipVerify のいずれかを必ず設定する必要があるという、より厳格な要件が追加されました。これにより、開発者が意図せず証明書検証をスキップしてしまう状況を防ぎ、より安全なTLS接続の確立を促します。

これらのドキュメンテーションの改善は、コードの動作自体を変更するものではありませんが、開発者が crypto/tls パッケージをより安全かつ効果的に使用するための重要なガイダンスを提供します。特に、TLSのセキュリティモデルにおいてホスト名検証がいかに重要であるかを再認識させ、一般的な誤用パターンを回避するための明確な指示を与えます。

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

このコミットでは、以下の2つのファイルが変更されています。

  1. src/pkg/crypto/tls/common.go
  2. src/pkg/crypto/tls/tls.go

src/pkg/crypto/tls/common.go の変更

Config 構造体の ServerName フィールドのコメントが変更されました。

--- a/src/pkg/crypto/tls/common.go
+++ b/src/pkg/crypto/tls/common.go
@@ -231,8 +231,9 @@ type Config struct {
 	// NextProtos is a list of supported, application level protocols.
 	NextProtos []string
 
-	// ServerName is included in the client's handshake to support virtual
-	// hosting.
+	// ServerName is used to verify the hostname on the returned
+	// certificates unless InsecureSkipVerify is given. It is also included
+	// in the client's handshake to support virtual hosting.
 	ServerName string
 
 	// ClientAuth determines the server's policy for

src/pkg/crypto/tls/tls.go の変更

Client 関数のコメントが変更されました。

--- a/src/pkg/crypto/tls/tls.go
+++ b/src/pkg/crypto/tls/tls.go
@@ -27,9 +27,8 @@ func Server(conn net.Conn, config *Config) *Conn {
 
 // Client returns a new TLS client side connection
 // using conn as the underlying transport.
-// Client interprets a nil configuration as equivalent to
-// the zero configuration; see the documentation of Config
-// for the defaults.
+// The config cannot be nil: users must set either ServerHostname or
+// InsecureSkipVerify in the config.
 func Client(conn net.Conn, config *Config) *Conn {
 	return &Conn{conn: conn, config: config, isClient: true}
 }

コアとなるコードの解説

src/pkg/crypto/tls/common.go の変更解説

Config 構造体の ServerName フィールドのドキュメンテーションは、その役割をより正確に反映するように更新されました。

変更前: ServerName は「クライアントのハンドシェイクに含まれ、仮想ホスティングをサポートする」とだけ説明されていました。これはSNIの機能としては正しいですが、GoのTLS実装における ServerName のもう一つの重要な役割、すなわち証明書のホスト名検証については触れられていませんでした。

変更後:ServerName は、InsecureSkipVerify が与えられない限り、返された証明書のホスト名を検証するために使用されます。また、仮想ホスティングをサポートするためにクライアントのハンドシェイクにも含まれます。」という説明が追加されました。

この変更により、開発者は ServerName が単なるSNIのためだけでなく、セキュリティ上重要な証明書検証の基準としても機能することを明確に理解できるようになります。これにより、ServerName を設定しないことが、意図しない証明書検証のスキップにつながる可能性があるという認識が高まります。

src/pkg/crypto/tls/tls.go の変更解説

Client 関数のドキュメンテーションは、config 引数の取り扱いに関する重要な制約を明確にするために変更されました。

変更前: Client 関数は、「nil の設定をゼロ設定と同等に解釈する」と説明されており、Config のデフォルトについては別途ドキュメンテーションを参照するよう促していました。これは、nil Config が許容されるという誤解を招く可能性がありました。

変更後:confignil にできません。ユーザーは config 内で ServerHostname (これは ServerName の誤記または古い名称) または InsecureSkipVerify のいずれかを設定する必要があります。」という、より厳格で具体的な指示に置き換えられました。

この変更は、Client 関数を呼び出す際に Config オブジェクトが必須であり、さらにその Config オブジェクト内で ServerName (ホスト名検証のため) または InsecureSkipVerify (検証を意図的にスキップするため) のいずれかが明示的に設定されている必要があることを強調しています。これにより、開発者がTLS接続のセキュリティ設定を意識的に行うよう促し、デフォルトで安全でない状態になることを防ぎます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメンテーション
  • TLSプロトコルに関する一般的な情報源 (例: Wikipedia)
  • コミットメッセージと差分 (diff)
  • Go言語のコードレビューシステム (Gerrit) の関連リンク: https://golang.org/cl/65440043 (現在は https://go.dev/cl/65440043 にリダイレクトされる可能性があります)