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

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

このコミットは、Go言語の公式ドキュメント「Effective Go」内のサンプルコードにおいて、テンプレートエンジンとして text/template パッケージから html/template パッケージへの移行を行うものです。これにより、ウェブページに表示されるコンテンツのセキュリティが向上し、クロスサイトスクリプティング (XSS) などの脆弱性に対する保護が強化されます。

コミット

commit f3fc0090f46b9e1cbcfb5f870d2d0ef59f6e4f31
Author: Rob Pike <r@golang.org>
Date:   Thu Sep 13 13:41:13 2012 -0700

    effective_go: use html/template instead of text/template
    Should have done this a long time ago.
    Fixes #3811.
    
    R=golang-dev, adg, rsc
    CC=golang-dev
    https://golang.org/cl/6488120

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

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

元コミット内容

このコミットは、effective_go ドキュメント内で使用されているテンプレートエンジンを text/template から html/template へ変更するものです。コミットメッセージには「ずいぶん前にこれを行うべきだった」とあり、これはセキュリティ上の改善が以前から認識されていたことを示唆しています。また、Issue #3811 を修正するものであると明記されています。

変更の背景

Go言語には、テキストベースの出力を生成するための text/template パッケージと、HTML出力を安全に生成するための html/template パッケージの2種類のテンプレートパッケージが存在します。

text/template は汎用的なテキスト生成に用いられ、入力されたデータに対して特別なエスケープ処理を行いません。そのため、ウェブアプリケーションでユーザーからの入力をそのままHTMLとして表示する際に、悪意のあるスクリプトが埋め込まれるクロスサイトスクリプティング (XSS) 脆弱性の原因となる可能性があります。

一方、html/template は、HTMLコンテキストでの使用を前提として設計されており、デフォルトで自動エスケープ機能を提供します。これにより、テンプレートに挿入されるデータがHTMLエンティティに変換され、ブラウザがそれをスクリプトとして解釈することを防ぎます。

「Effective Go」はGo言語のベストプラクティスを示す公式ドキュメントであり、その中のサンプルコードはGo言語の推奨される使い方を示すべきです。ウェブアプリケーションの文脈でテンプレートを使用する場合、セキュリティは非常に重要です。したがって、セキュリティリスクを伴う text/template ではなく、安全な html/template を使用することが、ドキュメントの品質と読者への適切なガイダンスのために必要でした。この変更は、Go言語のセキュリティに対する意識の高さと、公式ドキュメントの正確性を保つための継続的な努力を反映しています。

前提知識の解説

Go言語のテンプレートパッケージ (text/templatehtml/template)

Go言語の標準ライブラリには、テキストやHTMLを生成するためのテンプレートエンジンが用意されています。

  • text/template:

    • 汎用的なテキスト出力のためのパッケージです。
    • 入力されたデータに対して、HTMLエスケープなどの特別な処理を行いません。
    • 例えば、設定ファイル、メールの本文、プレーンテキストのレポートなど、HTML以外のテキスト形式を生成するのに適しています。
    • セキュリティ上の考慮なしに、任意の文字列をそのまま出力するため、ウェブアプリケーションでユーザー提供のデータを扱う際には注意が必要です。
  • html/template:

    • HTML出力を安全に生成するために特化されたパッケージです。
    • デフォルトでコンテキストに応じた自動エスケープ機能を提供します。これは、テンプレートに挿入されるデータが、それがHTML要素の属性値、テキストコンテンツ、JavaScriptコード、CSSなど、どのコンテキストで使用されるかに応じて適切にエスケープされることを意味します。
    • これにより、悪意のあるスクリプトの挿入(XSS)を防ぎ、ウェブアプリケーションのセキュリティを大幅に向上させます。
    • ウェブアプリケーションのフロントエンドを生成する際には、常に html/template を使用することが強く推奨されます。

クロスサイトスクリプティング (XSS)

クロスサイトスクリプティング (Cross-Site Scripting, XSS) は、ウェブアプリケーションの脆弱性の一種です。攻撃者がウェブサイトに悪意のあるスクリプト(通常はJavaScript)を注入し、そのスクリプトが他のユーザーのブラウザで実行されることで発生します。

XSS攻撃が成功すると、以下のような被害が発生する可能性があります。

  • セッションクッキーの盗難(ユーザーのログインセッションを乗っ取る)
  • ウェブサイトのコンテンツの改ざん
  • フィッシング詐プトの表示
  • ユーザーの個人情報の窃取
  • ブラウザの挙動の制御

XSSは、ウェブアプリケーションがユーザーからの入力を適切にサニタイズ(無害化)せずに、そのままHTMLとして出力してしまう場合に発生します。例えば、ユーザーがコメント欄に <script>alert('XSS!');</script> と入力し、それがそのままページに表示されると、他のユーザーがそのページを閲覧した際に alert('XSS!'); が実行されてしまいます。

html/template の自動エスケープ機能は、このような悪意のあるスクリプトがHTMLとして解釈されることを防ぎ、XSS攻撃を効果的に緩和します。

技術的詳細

このコミットの技術的な核心は、Go言語のテンプレート処理におけるセキュリティモデルの適用です。

doc/effective_go.html は、Go言語の「Effective Go」ドキュメントの一部であり、ウェブアプリケーションの例としてQRコード生成サービスが紹介されています。このサービスは、ユーザーが入力した文字列をQRコードとして表示し、その文字列をページ上にも表示します。

元の実装では、text/template パッケージが使用されていました。このパッケージは、テンプレート内の {{. }} のようなプレースホルダーにデータを挿入する際に、そのデータをそのまま出力します。例えば、ユーザーが s というフォーム値に <script>alert('XSS');</script> を入力した場合、{{. }} はこの文字列をそのままHTMLに出力してしまいます。

<!-- text/template の場合 -->
<img src="..." />
<br>
{{html .}} <!-- ここでエスケープされない -->
<br>

上記の {{html .}} は、text/templatehtml 関数であり、これはHTMLエスケープを行うための明示的な関数です。しかし、text/template はデフォルトでエスケープを行わないため、開発者が明示的に html 関数を呼び出す必要があります。もし呼び出しを忘れた場合、XSS脆弱性が発生します。

このコミットでは、doc/progs/eff_qr.go 内でインポートするパッケージを text/template から html/template に変更しています。

// 変更前
import (
	"flag"
	"log"
	"net/http"
	"text/template" // text/template をインポート
)

// 変更後
import (
	"flag"
	"html/template" // html/template をインポート
	"log"
	"net/http"
)

html/template を使用すると、テンプレート内の {{. }} やその他のアクションは、挿入されるデータのコンテキスト(HTML要素のテキスト、属性、URLなど)を自動的に判断し、適切なエスケープ処理を施します。

例えば、eff_qr.gotemplateStr 内の以下の部分が変更されています。

<!-- 変更前 -->
<img src="http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={{urlquery .}}" />
<br>
{{html .}}
<br>

<!-- 変更後 -->
<img src="http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={{.}}" />
<br>
{{.}}
<br>

変更前は {{urlquery .}}{{html .}} のように、明示的に urlquery 関数や html 関数を呼び出してエスケープ処理を行っていました。しかし、html/template を使用することで、これらの明示的なエスケープ関数は不要になります。html/template は、{{. }} がURLの一部として使用される場合はURLエスケープを、HTMLコンテンツとして使用される場合はHTMLエスケープを自動的に適用します。

これにより、開発者がエスケープ処理を意識することなく、安全なHTMLを生成できるようになります。これは、セキュリティのベストプラクティスである「デフォルトで安全 (secure by default)」の原則に則ったものです。

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

--- a/doc/effective_go.html
+++ b/doc/effective_go.html
@@ -2992,11 +2992,11 @@ server; it blocks while the server runs.\n executes the template on the data in the form value named <code>s</code>.\n </p>\n <p>\n-The template package is powerful;\n+The template package <code>html/template</code> is powerful;\n this program just touches on its capabilities.\n-In essence, it rewrites a piece of text on the fly by substituting elements derived\n+In essence, it rewrites a piece of HTML text on the fly by substituting elements derived\n from data items passed to <code>templ.Execute</code>, in this case the\n-form value.  \n+form value.\n Within the template text (<code>templateStr</code>),\n double-brace-delimited pieces denote template actions.\n The piece from <code>{{html \"{{if .}}\"}}</code>\n@@ -3005,13 +3005,14 @@ is non-empty.\n That is, when the string is empty, this piece of the template is suppressed.\n </p>\n <p>\n-The snippet <code>{{html \"{{urlquery .}}\"}}</code> says to process the data with the function\n-<code>urlquery</code>, which sanitizes the query string\n-for safe display on the web page.\n+The two snippets <code>{{html \"{{.}}\"}}</code> say to show the data presented to\n+the template—the query string—on the web page.\n+The HTML template package automatically provides appropriate escaping so the\n+text is safe to display.\n </p>\n <p>\n The rest of the template string is just the HTML to show when the page loads.\n-If this is too quick an explanation, see the <a href=\"/pkg/text/template/\">documentation</a>\n+If this is too quick an explanation, see the <a href=\"/pkg/html/template/\">documentation</a>\n for the template package for a more thorough discussion.\n </p>\n <p>\ndiff --git a/doc/progs/eff_qr.go b/doc/progs/eff_qr.go
index 4ac745c930..861131ddf5 100644
--- a/doc/progs/eff_qr.go
+++ b/doc/progs/eff_qr.go
@@ -8,9 +8,9 @@ package main\n \n import (\n \t\"flag\"\n+\t\"html/template\"\n \t\"log\"\n \t\"net/http\"\n-\t\"text/template\"\n )\n \n var addr = flag.String(\"addr\", \":1718\", \"http service address\") // Q=17, R=18\n@@ -37,9 +37,9 @@ const templateStr = `\n </head>\n <body>\n {{if .}}\n-<img src=\"http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={{urlquery .}}\" />\n+<img src=\"http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={{.}}\" />\n <br>\n-{{html .}}\n+{{.}}\n <br>\n <br>\n {{end}}\n```

## コアとなるコードの解説

このコミットは主に2つのファイルに変更を加えています。

1.  **`doc/effective_go.html`**:
    *   このファイルは「Effective Go」ドキュメントのHTMLソースです。
    *   変更点としては、テンプレートパッケージに関する説明文が更新されています。
        *   `The template package is powerful;` が `The template package <code>html/template</code> is powerful;` に変更され、具体的に `html/template` パッケージに言及するようになりました。
        *   `it rewrites a piece of text on the fly` が `it rewrites a piece of HTML text on the fly` に変更され、HTMLテキストを扱うことを明確にしています。
        *   `{{html "urlquery ."}}` の説明が `{{html "."}}` の説明に変わり、`html/template` が自動的に適切なエスケープを提供することを強調しています。
        *   テンプレートパッケージのドキュメントへのリンクが `/pkg/text/template/` から `/pkg/html/template/` に更新されています。
    *   これらの変更は、ドキュメントの内容を実際のコード変更に合わせて修正し、読者に `html/template` の利用を推奨する意図を明確にするものです。

2.  **`doc/progs/eff_qr.go`**:
    *   このファイルは、QRコード生成サービスのGo言語のサンプルプログラムです。
    *   **インポートの変更**:
        *   `- "text/template"`: `text/template` パッケージのインポートが削除されました。
        *   `+ "html/template"`: `html/template` パッケージが新しくインポートされました。
        *   これにより、プログラムが `html/template` の機能を使用するようになります。
    *   **テンプレート文字列 `templateStr` の変更**:
        *   `<img src="...&chl={{urlquery .}}" />` が `<img src="...&chl={{.}}" />` に変更されました。
            *   `text/template` では `urlquery` 関数を明示的に呼び出してURLエスケープを行う必要がありましたが、`html/template` は `chl` 属性のコンテキストを認識し、自動的にURLエスケープを適用するため、`urlquery` の呼び出しが不要になりました。
        *   `{{html .}}` が `{{.}}` に変更されました。
            *   `text/template` では `html` 関数を明示的に呼び出してHTMLエスケープを行う必要がありましたが、`html/template` はHTMLコンテンツのコンテキストを認識し、自動的にHTMLエスケープを適用するため、`html` の呼び出しが不要になりました。

これらの変更により、サンプルプログラムはより安全な `html/template` を使用するようになり、XSS脆弱性に対する保護が強化されました。また、ドキュメントもそれに合わせて更新され、読者に対してセキュリティを考慮したテンプレート利用のベストプラクティスを示しています。

## 関連リンク

*   Go言語 `html/template` パッケージ公式ドキュメント: [https://pkg.go.dev/html/template](https://pkg.go.dev/html/template)
*   Go言語 `text/template` パッケージ公式ドキュメント: [https://pkg.go.dev/text/template](https://pkg.go.dev/text/template)
*   Go言語 Effective Go ドキュメント: [https://go.dev/doc/effective_go](https://go.dev/doc/effective_go)

## 参考にした情報源リンク

*   OWASP Cross-Site Scripting (XSS): [https://owasp.org/www-community/attacks/xss/](https://owasp.org/www-community/attacks/xss/)
*   Go言語におけるテンプレートエンジンの使い分けとセキュリティ: (一般的なGo言語のセキュリティに関する記事やブログを参照)