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

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

このコミットは、Go言語の標準ライブラリであるtext/template(またはhtml/template)パッケージにおける、エラー型がテンプレート内で文字列として扱われる挙動を修正するものです。具体的には、テストコード内でos.NewErrorで作成されたエラーオブジェクトをbytes.NewBufferで作成されたバッファオブジェクトに置き換えることで、テンプレートエンジンがエラーオブジェクトを自動的に文字列化しないように変更しています。これにより、エラーメッセージが意図せずテンプレートに出力されることを防ぎ、セキュリティと堅牢性を向上させています。

コミット

  • コミットハッシュ: 853c84631f1afda1672930e8e509beeb0e9d44f9
  • Author: Russ Cox rsc@golang.org
  • Date: Thu Oct 27 21:17:47 2011 -0700
  • 変更ファイル: src/pkg/template/exec_test.go (1ファイル)
  • 変更行数: 2行 (1挿入, 1削除)

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

https://github.com/golang/go/commit/853c84631f1afda1672930e8e509beeb0e9d44f9

元コミット内容

template: do not use error as stringer

R=r
CC=golang-dev
https://golang.org/cl/5305069

変更の背景

Go言語のテンプレートパッケージ(text/templatehtml/template)は、テンプレート内で表示される値が特定のインターフェース(例えばfmt.Stringer)を実装している場合、そのインターフェースのメソッド(String())を呼び出して値を文字列としてレンダリングする機能を持っています。

初期のGo言語の設計では、errorインターフェースを実装する型(つまり、Error()メソッドを持つ型)が、テンプレート内で暗黙的にfmt.Stringerのように扱われ、そのError()メソッドの戻り値が直接テンプレートに出力される可能性がありました。

しかし、エラーメッセージには、ファイルパス、内部的な状態、スタックトレースなど、デバッグには有用でもエンドユーザーに公開すべきではない機密情報や内部情報が含まれている場合があります。このような情報が意図せずWebページやその他の出力に表示されてしまうと、情報漏洩やセキュリティ上の脆弱性につながる可能性があります。

このコミットは、このような潜在的なセキュリティリスクを軽減し、テンプレートの堅牢性を高めることを目的としています。エラーオブジェクトがテンプレート内で自動的に文字列化されることを防ぐことで、開発者はエラーを明示的に処理し、ユーザーに表示する内容を制御するよう促されます。これにより、不必要な情報が公開されることを防ぎ、アプリケーションのセキュリティが向上します。

前提知識の解説

Go言語のerrorインターフェース

Go言語では、エラーは組み込みのerrorインターフェースによって表現されます。このインターフェースは非常にシンプルで、Error() stringという単一のメソッドを定義しています。

type error interface {
    Error() string
}

この設計により、Goの関数はエラーが発生した場合にerror型の値を返すことができ、呼び出し元はそのエラーを処理することができます。

fmt.Stringerインターフェース

fmt.Stringerインターフェースは、Goのfmtパッケージで定義されており、値を人間が読める文字列形式に変換するためのString() stringメソッドを定義しています。

type Stringer interface {
    String() string
}

Goの多くの標準ライブラリ関数(例: fmt.Printfmt.Sprintf)は、引数がfmt.Stringerを実装している場合、そのString()メソッドを呼び出して文字列表現を取得します。

text/templateおよびhtml/templateパッケージの挙動

Goのテンプレートパッケージは、データ構造をテンプレートに渡してレンダリングする際に、渡された値の型を検査し、特定のインターフェースを実装している場合に特別な処理を行います。 特に、テンプレートエンジンは、表示しようとする値がfmt.Stringerインターフェースを実装している場合、そのString()メソッドを呼び出して得られた文字列をテンプレートに挿入します。

このコミット以前のGoのテンプレート実装では、errorインターフェースを実装する型が、暗黙的にfmt.Stringerのように扱われることがありました。これは、errorインターフェースのError()メソッドとfmt.StringerインターフェースのString()メソッドが、どちらも() stringという同じシグネチャを持つため、Goのインターフェースの仕組み上、error型がfmt.Stringerとしても振る舞うことが可能であったためです。

os.NewErrorbytes.NewBuffer

  • os.NewError(s string) error: Go 1.0より前のバージョンで存在した関数で、指定された文字列sをエラーメッセージとする新しいerrorオブジェクトを作成するために使用されていました。Go 1.0以降は非推奨となり、代わりにerrors.New(s string) errorが推奨されています。この関数が返すのは、errorインターフェースを実装する具体的な型です。

  • bytes.NewBuffer(buf []byte) *Buffer: bytesパッケージの関数で、バイトスライスbufを初期値とする新しいBufferオブジェクトを作成します。*bytes.Buffer型は、io.Readerio.Writerfmt.Stringerなど、多くのインターフェースを実装しています。特に、String() stringメソッドを実装しており、バッファの内容を文字列として返します。これは、テンプレート内で安全に文字列データを扱うための一般的な方法です。

技術的詳細

このコミットの技術的な核心は、Goのテンプレートエンジンが、errorインターフェースを実装するオブジェクトを、もはやfmt.Stringerとして自動的に扱わないようにするという方針転換にあります。

以前の挙動では、例えばテンプレート内で.ErrorFieldのようにエラーオブジェクトを参照した場合、そのエラーオブジェクトのError()メソッドが呼び出され、その結果がテンプレートに直接埋め込まれていました。これは、開発者がエラーを明示的に処理する代わりに、エラーメッセージがそのままユーザーインターフェースに表示されてしまうという、意図しない結果を招く可能性がありました。

この変更により、テンプレートエンジンはerror型を特別扱いし、そのError()メソッドを自動的に呼び出して文字列化することを停止します。これにより、テンプレート内でエラーオブジェクトを直接参照しても、何も出力されないか、あるいはエラー処理が明示的に行われていない場合にはテンプレートの実行が失敗するようになります。

テストコードにおけるos.NewError("foozle")からbytes.NewBuffer([]byte("foozle"))への変更は、この新しい挙動を反映したものです。

  • os.NewError("foozle")error型を生成します。この型は、変更前はテンプレートによって文字列化されていました。
  • bytes.NewBuffer([]byte("foozle"))*bytes.Buffer型を生成します。この型はfmt.Stringerを明示的に実装しており、そのString()メソッドはバッファの内容(この場合は"foozle")を返します。したがって、このオブジェクトはテンプレートによって安全に文字列化されます。

この変更は、テンプレートのセキュリティモデルを強化し、開発者がエラー処理と情報公開についてより意識的な決定を下すことを強制します。これにより、アプリケーションがより堅牢になり、潜在的な情報漏洩のリスクが低減されます。

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

変更はsrc/pkg/template/exec_test.goファイル内のテストデータ定義にあります。

--- a/src/pkg/template/exec_test.go
+++ b/src/pkg/template/exec_test.go
@@ -98,7 +98,7 @@ var tVal = &T{
  	Empty3:            []int{7, 8},\n  	Empty4:            &U{"UinEmpty"},\n  	NonEmptyInterface: new(T),\n-\tStr:               os.NewError("foozle"),\n+\tStr:               bytes.NewBuffer([]byte("foozle")),\n  	PI:                newInt(23),\n  	PSI:               newIntSlice(21, 22, 23),\n  	Tmpl:              Must(New("x").Parse("test template")), // "x" is the value of .X

具体的には、tValというテスト用の構造体Tの初期化において、Strフィールドの値が変更されています。

  • 変更前: Str: os.NewError("foozle")
  • 変更後: Str: bytes.NewBuffer([]byte("foozle"))

コアとなるコードの解説

この変更は、templateパッケージのテストスイートの一部であるexec_test.goファイル内のtValというグローバル変数に定義されているテストデータT構造体のStrフィールドの値を修正しています。

tValは、テンプレートの実行テストにおいて、テンプレートに渡されるデータコンテキストとして使用されるものです。Strフィールドは、テンプレートが文字列としてレンダリングすることを期待する値を保持するために使用されていました。

  • 変更前 (os.NewError("foozle")): この行は、Strフィールドにos.NewError関数によって作成されたerror型のオブジェクトを割り当てていました。このerrorオブジェクトは、Error()メソッドを呼び出すと文字列"foozle"を返します。変更前のテンプレートエンジンの挙動では、このerrorオブジェクトがテンプレート内で参照された際に、そのError()メソッドが自動的に呼び出され、結果として"foozle"という文字列がテンプレートに出力されることが期待されていました。これは、error型がfmt.Stringerのように扱われることをテストしていた、あるいはその挙動に依存していたことを示唆しています。

  • 変更後 (bytes.NewBuffer([]byte("foozle"))): この行は、Strフィールドにbytes.NewBuffer関数によって作成された*bytes.Buffer型のオブジェクトを割り当てています。*bytes.Buffer型は、fmt.Stringerインターフェースを明示的に実装しており、そのString()メソッドはバッファの内容(この場合はバイトスライス[]byte("foozle")が文字列に変換されたもの)を返します。 この変更は、テンプレートエンジンがerror型を自動的に文字列化しないようになった新しい挙動に合わせて、テストデータを調整したものです。つまり、Strフィールドがテンプレート内で文字列としてレンダリングされることを引き続きテストするために、error型ではなく、fmt.Stringerを安全に実装している*bytes.Buffer型を使用するように変更されました。

このテストコードの変更は、templateパッケージの内部的な挙動変更(エラー型をstringerとして扱わない)が正しく機能していることを確認するためのものです。これにより、開発者はテンプレート内でエラーオブジェクトを直接表示するのではなく、エラーを適切に処理し、ユーザーに表示する内容を明示的に制御する必要があるという、より安全なプログラミングプラクティスが促進されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント(上記「関連リンク」に記載の各パッケージドキュメント)
  • Go言語のコミット履歴と関連するコードレビューディスカッション
  • Go言語のテンプレートにおけるセキュリティに関する一般的な情報源(例: Goのテンプレートエスケープメカニズムに関する記事など)

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

このコミットは、Go言語の標準ライブラリであるtext/template(またはhtml/template)パッケージにおいて、エラー型がテンプレート内で文字列として扱われる挙動を修正するものです。具体的には、テストコード内でos.NewErrorで作成されたエラーオブジェクトをbytes.NewBufferで作成されたバッファオブジェクトに置き換えることで、テンプレートエンジンがエラーオブジェクトを自動的に文字列化しないように変更しています。これにより、エラーメッセージが意図せずテンプレートに出力されることを防ぎ、セキュリティと堅牢性を向上させています。

コミット

  • コミットハッシュ: 853c84631f1afda1672930e8e509beeb0e9d44f9
  • Author: Russ Cox rsc@golang.org
  • Date: Thu Oct 27 21:17:47 2011 -0700
  • 変更ファイル: src/pkg/template/exec_test.go (1ファイル)
  • 変更行数: 2行 (1挿入, 1削除)

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

https://github.com/golang/go/commit/853c84631f1afda1672930e8e509beeb0e9d44f9

元コミット内容

template: do not use error as stringer

R=r
CC=golang-dev
https://golang.org/cl/5305069

変更の背景

Go言語のテンプレートパッケージ(text/templatehtml/template)は、テンプレート内で表示される値が特定のインターフェース(例えばfmt.Stringer)を実装している場合、そのインターフェースのメソッド(String())を呼び出して値を文字列としてレンダリングする機能を持っています。

初期のGo言語の設計では、errorインターフェースを実装する型(つまり、Error()メソッドを持つ型)が、テンプレート内で暗黙的にfmt.Stringerのように扱われ、そのError()メソッドの戻り値が直接テンプレートに出力される可能性がありました。これは、errorインターフェースのError()メソッドとfmt.StringerインターフェースのString()メソッドが、どちらも() stringという同じシグネチャを持つため、Goのインターフェースの仕組み上、error型がfmt.Stringerとしても振る舞うことが可能であったためです。

しかし、エラーメッセージには、ファイルパス、内部的な状態、スタックトレースなど、デバッグには有用でもエンドユーザーに公開すべきではない機密情報や内部情報が含まれている場合があります。このような情報が意図せずWebページやその他の出力に表示されてしまうと、情報漏洩やクロスサイトスクリプティング(XSS)などのセキュリティ上の脆弱性につながる可能性があります。特に、text/templateは自動的なエスケープを行わないため、悪意のあるスクリプトがエラーメッセージに含まれていた場合、XSS攻撃のリスクが高まります。

このコミットは、このような潜在的なセキュリティリスクを軽減し、テンプレートの堅牢性を高めることを目的としています。エラーオブジェクトがテンプレート内で自動的に文字列化されることを防ぐことで、開発者はエラーを明示的に処理し、ユーザーに表示する内容を制御するよう促されます。これにより、不必要な情報が公開されることを防ぎ、アプリケーションのセキュリティが向上します。

前提知識の解説

Go言語のerrorインターフェース

Go言語では、エラーは組み込みのerrorインターフェースによって表現されます。このインターフェースは非常にシンプルで、Error() stringという単一のメソッドを定義しています。

type error interface {
    Error() string
}

この設計により、Goの関数はエラーが発生した場合にerror型の値を返すことができ、呼び出し元はそのエラーを処理することができます。

fmt.Stringerインターフェース

fmt.Stringerインターフェースは、Goのfmtパッケージで定義されており、値を人間が読める文字列形式に変換するためのString() stringメソッドを定義しています。

type Stringer interface {
    String() string
}

Goの多くの標準ライブラリ関数(例: fmt.Printfmt.Sprintf)は、引数がfmt.Stringerを実装している場合、そのString()メソッドを呼び出して文字列表現を取得します。

text/templateおよびhtml/templateパッケージの挙動

Goのテンプレートパッケージは、データ構造をテンプレートに渡してレンダリングする際に、渡された値の型を検査し、特定のインターフェースを実装している場合に特別な処理を行います。 特に、テンプレートエンジンは、表示しようとする値がfmt.Stringerインターフェースを実装している場合、そのString()メソッドを呼び出して得られた文字列をテンプレートに挿入します。

このコミット以前のGoのテンプレート実装では、errorインターフェースを実装する型が、暗黙的にfmt.Stringerのように扱われることがありました。これは、errorインターフェースのError()メソッドとfmt.StringerインターフェースのString()メソッドが、どちらも() stringという同じシグネチャを持つため、Goのインターフェースの仕組み上、error型がfmt.Stringerとしても振る舞うことが可能であったためです。

os.NewErrorbytes.NewBuffer

  • os.NewError(s string) error: Go 1.0より前のバージョンで存在した関数で、指定された文字列sをエラーメッセージとする新しいerrorオブジェクトを作成するために使用されていました。Go 1.0以降は非推奨となり、代わりにerrors.New(s string) errorが推奨されています。この関数が返すのは、errorインターフェースを実装する具体的な型です。

  • bytes.NewBuffer(buf []byte) *Buffer: bytesパッケージの関数で、バイトスライスbufを初期値とする新しいBufferオブジェクトを作成します。*bytes.Buffer型は、io.Readerio.Writerfmt.Stringerなど、多くのインターフェースを実装しています。特に、String() stringメソッドを実装しており、バッファの内容を文字列として返します。これは、テンプレート内で安全に文字列データを扱うための一般的な方法です。

技術的詳細

このコミットの技術的な核心は、Goのテンプレートエンジンが、errorインターフェースを実装するオブジェクトを、もはやfmt.Stringerとして自動的に扱わないようにするという方針転換にあります。

以前の挙動では、例えばテンプレート内で.ErrorFieldのようにエラーオブジェクトを参照した場合、そのエラーオブジェクトのError()メソッドが呼び出され、その結果がテンプレートに直接埋め込まれていました。これは、開発者がエラーを明示的に処理する代わりに、エラーメッセージがそのままユーザーインターフェースに表示されてしまうという、意図しない結果を招く可能性がありました。

この変更により、テンプレートエンジンはerror型を特別扱いし、そのError()メソッドを自動的に呼び出して文字列化することを停止します。これにより、テンプレート内でエラーオブジェクトを直接参照しても、何も出力されないか、あるいはエラー処理が明示的に行われていない場合にはテンプレートの実行が失敗するようになります。

テストコードにおけるos.NewError("foozle")からbytes.NewBuffer([]byte("foozle"))への変更は、この新しい挙動を反映したものです。

  • os.NewError("foozle")error型を生成します。この型は、変更前はテンプレートによって文字列化されていましたが、変更後は自動的に文字列化されなくなります。
  • bytes.NewBuffer([]byte("foozle"))*bytes.Buffer型を生成します。この型はfmt.Stringerを明示的に実装しており、そのString()メソッドはバッファの内容(この場合は"foozle")を返します。したがって、このオブジェクトはテンプレートによって安全に文字列化されます。

この変更は、テンプレートのセキュリティモデルを強化し、開発者がエラー処理と情報公開についてより意識的な決定を下すことを強制します。これにより、アプリケーションがより堅牢になり、潜在的な情報漏洩のリスクが低減されます。特に、Webアプリケーションにおいては、html/templateパッケージを常に使用し、String()メソッドの実装が機密情報を公開しないように注意することが、XSSなどの脆弱性から保護するための重要なベストプラクティスとなります。

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

変更はsrc/pkg/template/exec_test.goファイル内のテストデータ定義にあります。

--- a/src/pkg/template/exec_test.go
+++ b/src/pkg/template/exec_test.go
@@ -98,7 +98,7 @@ var tVal = &T{
  	Empty3:            []int{7, 8},\n  	Empty4:            &U{"UinEmpty"},\n  	NonEmptyInterface: new(T),\n-\tStr:               os.NewError("foozle"),\n+\tStr:               bytes.NewBuffer([]byte("foozle")),\n  	PI:                newInt(23),\n  	PSI:               newIntSlice(21, 22, 23),\n  	Tmpl:              Must(New("x").Parse("test template")), // "x" is the value of .X

具体的には、tValというテスト用の構造体Tの初期化において、Strフィールドの値が変更されています。

  • 変更前: Str: os.NewError("foozle")
  • 変更後: Str: bytes.NewBuffer([]byte("foozle"))

コアとなるコードの解説

この変更は、templateパッケージのテストスイートの一部であるexec_test.goファイル内のtValというグローバル変数に定義されているテストデータT構造体のStrフィールドの値を修正しています。

tValは、テンプレートの実行テストにおいて、テンプレートに渡されるデータコンテキストとして使用されるものです。Strフィールドは、テンプレートが文字列としてレンダリングすることを期待する値を保持するために使用されていました。

  • 変更前 (os.NewError("foozle")): この行は、Strフィールドにos.NewError関数によって作成されたerror型のオブジェクトを割り当てていました。このerrorオブジェクトは、Error()メソッドを呼び出すと文字列"foozle"を返します。変更前のテンプレートエンジンの挙動では、このerrorオブジェクトがテンプレート内で参照された際に、そのError()メソッドが自動的に呼び出され、結果として"foozle"という文字列がテンプレートに出力されることが期待されていました。これは、error型がfmt.Stringerのように扱われることをテストしていた、あるいはその挙動に依存していたことを示唆しています。

  • 変更後 (bytes.NewBuffer([]byte("foozle"))): この行は、Strフィールドにbytes.NewBuffer関数によって作成された*bytes.Buffer型のオブジェクトを割り当てています。*bytes.Buffer型は、fmt.Stringerインターフェースを明示的に実装しており、そのString()メソッドはバッファの内容(この場合はバイトスライス[]byte("foozle")が文字列に変換されたもの)を返します。 この変更は、テンプレートエンジンがerror型を自動的に文字列化しないようになった新しい挙動に合わせて、テストデータを調整したものです。つまり、Strフィールドがテンプレート内で文字列としてレンダリングされることを引き続きテストするために、error型ではなく、fmt.Stringerを安全に実装している*bytes.Buffer型を使用するように変更されました。

このテストコードの変更は、templateパッケージの内部的な挙動変更(エラー型をstringerとして扱わない)が正しく機能していることを確認するためのものです。これにより、開発者はテンプレート内でエラーオブジェクトを直接表示するのではなく、エラーを適切に処理し、ユーザーに表示する内容を明示的に制御する必要があるという、より安全なプログラミングプラクティスが促進されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント(上記「関連リンク」に記載の各パッケージドキュメント)
  • Go言語のコミット履歴と関連するコードレビューディスカッション
  • Web検索結果: "golang template error as stringer security"
    • go.dev (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE4Mtnaot9PFGv11dtJjd-p_SlRpefqzANv6C9kX8OvKpI7L2ff4_g1mwembB7qdlntRpLWUFY1e4FT3zZB5W0lRyMkT0YyGMREntZRqwRrAj41CXaequg27jk=)
    • last9.io (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGRbbCfRFtzWKAl1uBx7wYeAv5uv7WBFNqVkfaJBmg_9nbqR6qn1Eb-YlFOSsbFQ0Q8Ml38NY8A12na8X4hOOxkDQyg7hqJLfqNSbhYMgOwLsJaqphhi5xQqUeYlb-Rjo9WYZl9j-s=)
    • go.dev (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJWTJf_ln9UUZJS-OkanJ2oTBCt5tt6RV7XLGrsPJdN4zJYDvYUGfCpy4kcUPE3zMZZfIWOZ8nCBNH3ndtPwoSC1K7F9qPUgWn9rTGnWiC1gMO1UsKlo2QeJE_0XyiWqojyP-x)
    • medium.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHkYIFSWHdUvOn3dWI0VUyOrPGhGyySbmvkT5EcS_t5374IkfWIfxRPPVzAPGPrZKNBBgOG_Rq3ILeSankdnpxDa4oNQ5IAW4-uWlsoO0iCPZ9tV8RojZSoJRWQ==)
    • labex.io (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE6TXp5KAY0odyLwd-HC_SLSbsn66wlyY9iyAVbQwmpaosN8SoT9xnubcHFPmoa-a4Kh5GGAPBsGkUaqpJJ8xYbSrqgqg17RLd04392kj01BJ1Ormb0-9P80ZG8nn66rQvLe-mJcls6zQV0x3-R_nKd6N698H9BGsXdZ-81ltG8HfKHSu56wfgi4g==)
    • studyraid.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHFRaYpniH-vlhRmZPVMilk5IUAFrEST2LkoeBf7LkamDciU-PURTOUhk9N0X8RUTkuRHU_BzjxOCjfnLsSbMqG8YcGrY9p3Xowvs8q68DSv1klBi3Db5AX3jDbhHr0OhkiMMUXNElX-UgKFw1NVsVaGSZCbJahB6P1aiWNPH6F_OjDI4TtojdRwL0lWe17cKxTKg==)
    • medium.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFeEdLREmwr-r3l3QdXXddVJMhzUt81ERD7OXSN4_qKZjlycHpfCN1E5RKhOHvspqswzJ-mVP64VYtqsq_vJ_2Xy90mUvd8U4iUYzfpF0bmPWTDpbS2ccVfaUmGu0DNjMsh0R9TyBpB5V0mlzj6Z_dd64UvVjgs79vy8XCTaIWVE45ueYGEwFheYNI=)