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

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

このコミットは、Go言語のhtmlパッケージおよびexp/htmlパッケージにおけるHTMLエスケープ処理の改善に関するものです。具体的には、シングルクォート(')とダブルクォート(")のエスケープ方法を、従来のエンティティ参照('")から数値文字参照('")に変更しています。これにより、Internet Explorer 8(IE8)以前のブラウザでの互換性問題が解決され、text/templateパッケージのHTMLEscape関数との一貫性が確保されます。

コミット

commit 6277656d69640da9166bbac2a132a3ddee61dcac
Author: Nigel Tao <nigeltao@golang.org>
Date:   Thu Apr 12 09:35:43 2012 +1000

    html, exp/html: escape ' and " as &#39; and &#34;, since IE8 and
    below do not support &apos;.
    
    This makes package html consistent with package text/template's
    HTMLEscape function.
    
    Fixes #3489.
    
    R=rsc, mikesamuel, dsymonds
    CC=golang-dev
    https://golang.org/cl/5992071

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

https://github.com/golang/go/commit/6277656d69640da9166bbac2a132a3ddee61dcac

元コミット内容

html, exp/html: escape ' and " as &#39; and &#34;, since IE8 and below do not support &apos;. This makes package html consistent with package text/template's HTMLEscape function. Fixes #3489.

(日本語訳) html, exp/html: IE8以前が&apos;をサポートしないため、'"&#39;&#34;としてエスケープする。 これにより、htmlパッケージがtext/templateパッケージのHTMLEscape関数と一貫性を持つようになる。 Issue #3489を修正。

変更の背景

この変更の主な背景は、Internet Explorer 8(IE8)およびそれ以前のバージョンのブラウザが、HTMLの標準で定義されているはずの文字実体参照&apos;(アポストロフィ、シングルクォート)を正しく解釈しないという互換性問題にありました。

HTML 4.01までは&apos;は標準で定義されておらず、XMLやXHTMLで使われるエンティティでした。HTML5で正式に導入されましたが、古いブラウザ、特にIE8以前ではサポートされていませんでした。そのため、Go言語のhtmlパッケージが&apos;を使用してシングルクォートをエスケープすると、IE8以前のブラウザで表示が崩れるなどの問題が発生する可能性がありました。

また、Go言語の標準ライブラリには、HTMLエスケープを行う複数のパッケージが存在します。htmlパッケージとtext/templateパッケージはその代表例です。このコミット以前は、htmlパッケージが&apos;&quot;を使用していたのに対し、text/templateパッケージは既に数値文字参照である&#39;&#34;を使用しており、両者間で一貫性がありませんでした。この不一致は、開発者がどちらのパッケージを使用するかによって異なるエスケープ結果になるという混乱を招く可能性がありました。

このコミットは、これらの問題を解決するために、より互換性の高い数値文字参照(&#39;&#34;)を使用するように変更し、同時に標準ライブラリ内でのエスケープ処理の一貫性を高めることを目的としています。

前提知識の解説

HTMLエスケープとは

HTMLエスケープとは、HTMLドキュメント内で特殊な意味を持つ文字(例: <>&"')を、その文字自体として表示するために、特別な表記(文字実体参照や数値文字参照)に変換する処理のことです。これにより、ブラウザがこれらの文字をHTMLタグやエンティティの開始として誤って解釈することを防ぎ、セキュリティ上の脆弱性(例: クロスサイトスクリプティング XSS)を防ぐ役割も果たします。

  • 文字実体参照 (Named Character Reference): &で始まり;で終わる、文字の名前を使った参照です。例: &lt;<)、&gt;>)、&amp;&)、&quot;")、&apos;')。
  • 数値文字参照 (Numeric Character Reference): &#で始まり;で終わる、文字のUnicodeコードポイントを使った参照です。10進数(&#DDDD;)または16進数(&#xHHHH;)で表記されます。例: &#60;<)、&#62;>)、&#38;&)、&#34;")、&#39;')。

&apos;&#39;の互換性問題

前述の通り、&apos;はHTML 4.01の標準では定義されていませんでした。XMLやXHTMLでは標準でしたが、HTML5で初めてHTMLの標準として導入されました。そのため、HTML5以前の仕様に準拠している、またはその仕様を厳密に解釈する古いブラウザ(特にIE8以前)では、&apos;を認識せず、そのまま文字列として表示してしまう、あるいは表示が崩れるといった問題が発生しました。

一方、&#39;のような数値文字参照は、HTMLのバージョンに関わらず、Unicodeコードポイントに基づいて文字を表現するため、ほとんどのブラウザで広くサポートされています。これは、ブラウザが文字の名前を知らなくても、その数値表現を解釈できるためです。

Go言語のhtmlパッケージとtext/templateパッケージ

  • htmlパッケージ: HTMLドキュメントのパース、レンダリング、エスケープなど、HTML関連の低レベルな処理を提供します。
  • text/templateパッケージ: テキストベースのテンプレートエンジンを提供し、HTML出力にも利用されます。このパッケージには、HTMLエスケープを行うHTMLEscape関数が含まれています。

このコミット以前は、htmlパッケージが&apos;&quot;を使用し、text/templateパッケージが&#39;&#34;を使用していたため、同じGo言語の標準ライブラリ内でエスケープの挙動が異なっていました。

Go言語のIssueトラッカー (#3489)

Go言語のIssueトラッカーは、バグ報告、機能要望、議論などを管理するためのシステムです。Fixes #3489という記述は、このコミットがGo言語のIssue #3489で報告された問題を解決したことを意味します。Issue #3489は、まさにIE8における&apos;の互換性問題に関するものでした。

技術的詳細

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

  • src/pkg/exp/html/escape.go
  • src/pkg/exp/html/render_test.go
  • src/pkg/exp/html/token_test.go
  • src/pkg/html/escape.go
  • src/pkg/net/http/server.go
  • src/pkg/text/template/funcs.go

主要な変更点は、シングルクォート(')とダブルクォート(")のエスケープ処理です。

  1. htmlおよびexp/htmlパッケージのエスケープ処理の変更:

    • 'のエスケープを&apos;から&#39;に変更。コメントで「&#39;&apos;よりも短く、aposはHTML5までHTMLにはなかった」と説明されています。
    • "のエスケープを&quot;から&#34;に変更。コメントで「&#34;&quot;よりも短い」と説明されています。
    • EscapeString関数のコメントも更新され、エスケープされる文字が「amp, apos, lt, gt and quot」から「<, >, &, ' and "」と、より直接的な表記に変更されています。
  2. net/http/server.goの変更:

    • htmlReplacerという文字列置換マップが更新され、"'のエスケープが同様に&#34;&#39;に変更されています。ここでも同様のコメントが追加されています。
  3. text/template/funcs.goの変更:

    • htmlAposの定義に関するコメントが更新され、「&#39;&apos;よりも短く、aposはHTML5までHTMLにはなかった」という説明が追加されています。これは、text/templateパッケージが既に&#39;を使用していたため、その理由を明確にするものです。
  4. テストファイルの更新:

    • src/pkg/exp/html/render_test.gosrc/pkg/exp/html/token_test.goでは、エスケープ結果の期待値が&quot;&apos;から&#34;&#39;に更新されています。これにより、新しいエスケープロジックが正しく機能することを確認しています。
    • src/pkg/exp/html/token_test.goTestUnescapeEscape関数では、テストロジックが改善され、UnescapeString(EscapeString(s))の結果が元の文字列sと一致するかどうかをより厳密にチェックするようになっています。

これらの変更により、Go言語のHTMLエスケープ処理は、古いブラウザとの互換性を向上させ、かつ標準ライブラリ全体での一貫性を確保しています。数値文字参照は、文字実体参照よりも一般的に互換性が高く、またバイト数も短くなる場合があるため、パフォーマンス面でもわずかながらメリットがある可能性があります。

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

src/pkg/exp/html/escape.go および src/pkg/html/escape.go

--- a/src/pkg/exp/html/escape.go
+++ b/src/pkg/exp/html/escape.go
@@ -205,13 +205,15 @@ func escape(w writer, s string) error {
 		case '&':
 			esc = "&amp;"
 		case '\'':
-			esc = "&apos;"
+			// "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
+			esc = "&#39;"
 		case '<':
 			esc = "&lt;"
 		case '>':
 			esc = "&gt;"
 		case '"':
-			esc = "&quot;"
+			// "&#34;" is shorter than "&quot;".
+			esc = "&#34;"
 		default:
 			panic("unrecognized escape character")
 		}
@@ -226,7 +228,7 @@ func escape(w writer, s string) error {
 }
 
 // EscapeString escapes special characters like "<" to become "&lt;". It
-// escapes only five such characters: amp, apos, lt, gt and quot.
+// escapes only five such characters: <, >, &, ' and ".
 // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
 // always true.
 func EscapeString(s string) string {

src/pkg/net/http/server.go

--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -785,8 +785,10 @@ var htmlReplacer = strings.NewReplacer(
 	"&", "&amp;",
 	"<", "&lt;",
 	">", "&gt;",
-	`"`, "&quot;",
-	"'", "&apos;",
+	// "&#34;" is shorter than "&quot;".
+	`"`, "&#34;",
+	// "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
+	"'", "&#39;",
 )
 
 func htmlEscape(s string) string {

コアとなるコードの解説

上記のコードスニペットは、Go言語のHTMLエスケープ処理における核心的な変更を示しています。

  1. escape 関数内の変更 (src/pkg/exp/html/escape.go および src/pkg/html/escape.go):

    • この関数は、HTML特殊文字をエスケープするための主要なロジックを含んでいます。
    • case '\'': のブロックでは、シングルクォート(')が検出された場合のエスケープ文字列が &apos; から &#39; に変更されています。追加されたコメントは、この変更の理由として「&#39;&apos;よりも短く、aposはHTML5までHTMLにはなかった」ことを明確にしています。これは、古いブラウザとの互換性向上と、エスケープ結果のバイト数削減という二重の目的を示唆しています。
    • case '"': のブロックでは、ダブルクォート(")が検出された場合のエスケープ文字列が &quot; から &#34; に変更されています。同様に、「&#34;&quot;よりも短い」というコメントが追加されており、バイト数削減の意図が伺えます。
    • EscapeString関数のコメントも更新され、エスケープされる文字がより直接的な表記に変更されています。これは、ユーザーや開発者に対して、この関数が具体的にどの文字をエスケープするのかを明確に伝えるための改善です。
  2. htmlReplacer の変更 (src/pkg/net/http/server.go):

    • net/httpパッケージ内のhtmlReplacerは、HTTPサーバーがHTMLコンテンツを生成する際に使用される文字列置換マップです。
    • ここでも、ダブルクォートとシングルクォートのエスケープ定義が、それぞれ&quot;から&#34;&apos;から&#39;に変更されています。これにより、net/httpパッケージも新しいエスケープ規則に準拠し、Go言語全体でのHTMLエスケープの一貫性が保たれます。コメントもescape.goと同様の理由を説明しています。

これらの変更は、Go言語のHTMLエスケープ処理をより堅牢で互換性の高いものにし、特に古いブラウザ環境での表示問題を回避することを目的としています。また、数値文字参照を使用することで、エスケープされた文字列の長さが短くなる可能性があり、これはわずかながらパフォーマンス上の利点にもなり得ます。

関連リンク

参考にした情報源リンク