[インデックス 10046] ファイルの概要
コミット
このコミットは、Go言語のHTML/templateパッケージにおけるCSSエスケープ機能の重要なバグ修正を行っています。具体的には、CSS形式のエスケープ処理において、エスケープ後に続く空白文字を適切に処理する問題を修正しました。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fc3ce34903d5f86f398eda87ca6e334f483df604
元コミット内容
commit fc3ce34903d5f86f398eda87ca6e334f483df604
Author: Mike Samuel <mikesamuel@gmail.com>
Date: Tue Oct 18 17:01:42 2011 -0500
exp/template/html: fix bug in cssEscaper
cssEscaper escapes using the CSS convention: `\` + hex + optional-space
It outputs the space when the escape could be followed by
a hex digit to distinguish a "\na" from "\u00aa".
It did not output a space when the escape is followed by a space
character so did not distinguish "\n " from "\n".
Currently when doing lookahead, it does not distinguish spaces that
will be escaped later by the same function from ones that will not.
This is correct but suboptimal.
R=nigeltao
CC=golang-dev
https://golang.org/cl/5306042
変更の背景
このバグ修正は、Go言語の実験的なHTMLテンプレートエンジン(後に正式版となる)において、CSS内での文字エスケープ処理に関する重要な問題を解決するものです。当時のGoは急速に発展しており、WebアプリケーションでのXSS攻撃を防ぐためのテンプレートエンジンのセキュリティ強化が急務でした。
特に、CSS内の文字エスケープは、HTML注入攻撃やCSS注入攻撃を防ぐ重要な防御メカニズムです。不適切なエスケープ処理により、攻撃者が悪意のあるコードを注入する可能性があり、これは深刻なセキュリティ脆弱性となります。
前提知識の解説
CSS文字エスケープの基礎
CSS仕様では、特殊文字は以下の形式でエスケープされます:
\
+ 16進数の文字コード + オプションの空白文字
この仕組みにより、CSS内で特殊な意味を持つ文字(例:{
、}
、;
など)を安全に表現できます。
空白文字の重要性
CSS エスケープにおいて、空白文字は以下の役割を果たします:
- 区切り文字として: エスケープされた文字と後続の文字を区別する
- 曖昧さの解消: 16進数のエスケープ後に16進数文字が続く場合の境界を明確にする
例:
\na
→ これは\n
+a
なのか\naa
なのか?\n a
→ 明確に\n
+a
と解釈される
HTML/templateパッケージの役割
Go言語のhtml/templateパッケージは、以下の機能を提供します:
- コンテキスト認識エスケープ: HTMLの各コンテキスト(HTML、CSS、JavaScript、URL)に応じて適切なエスケープを実行
- XSS防護: 悪意のあるスクリプト注入を防ぐ
- 型安全性: テンプレートの型チェック機能
CSS空白文字の定義
CSS仕様では、以下の文字が空白文字として定義されています:
\t
(タブ)\n
(改行)\f
(フォームフィード)\r
(キャリッジリターン)
技術的詳細
修正前の問題
修正前のコードでは、以下の問題がありました:
- 16進数文字の後続チェック: エスケープ後に16進数文字が続く場合のみ空白を挿入
- 空白文字の見落とし: エスケープ後に空白文字が続く場合を考慮していない
- 曖昧なエスケープ:
\n
(改行+空白) と\n
(改行のみ) の区別ができない
修正後の改善
修正後のコードでは:
- 包括的チェック: 16進数文字と空白文字の両方をチェック
- 明確な区別: エスケープされた文字と後続の文字を確実に区別
- 仕様準拠: CSS仕様に完全に準拠したエスケープ処理
コアとなるコードの変更箇所
1. isCSSSpace関数の追加
// isCSSSpace returns whether b is a CSS space char as defined in wc.
func isCSSSpace(b byte) bool {
switch b {
case '\t', '\n', '\f', '\r', ' ':
return true
}
return false
}
場所: src/pkg/exp/template/html/css.go:35-42
2. cssEscaper関数の修正
// 修正前
if repl != `\\` && (written == len(s) || isHex(s[written])) {
b.WriteByte(' ')
}
// 修正後
if repl != `\\` && (written == len(s) || isHex(s[written]) || isCSSSpace(s[written])) {
b.WriteByte(' ')
}
場所: src/pkg/exp/template/html/css.go:52
3. テストケースの強化
// テストで双方向チェックを追加
recoded := cssEscaper(got1)
if got2 := string(decodeCSS([]byte(recoded))); got2 != test.want {
t.Errorf("%q: escape & decode not dual for %q", test.css, recoded)
}
場所: src/pkg/exp/template/html/css_test.go:71-73
コアとなるコードの解説
isCSSSpace関数
この関数は、CSS仕様で定義されている空白文字を識別します:
func isCSSSpace(b byte) bool {
switch b {
case '\t', '\n', '\f', '\r', ' ':
return true
}
return false
}
- 目的: バイト値がCSS空白文字かどうかを判定
- 対象文字: タブ、改行、フォームフィード、キャリッジリターン、スペース
- 重要性: エスケープ処理の曖昧さを解消するために必要
cssEscaper関数の修正
修正の核心部分:
if repl != `\\` && (written == len(s) || isHex(s[written]) || isCSSSpace(s[written])) {
b.WriteByte(' ')
}
この条件は以下の場合に空白文字を挿入します:
repl !=
\``: バックスラッシュのエスケープでない場合written == len(s)
: 文字列の最後に到達した場合isHex(s[written])
: 次の文字が16進数の場合isCSSSpace(s[written])
: 次の文字がCSS空白文字の場合(新規追加)
テストケースの改善
修正後のテストでは、エスケープとデコードの双方向性を確認:
recoded := cssEscaper(got1)
if got2 := string(decodeCSS([]byte(recoded))); got2 != test.want {
t.Errorf("%q: escape & decode not dual for %q", test.css, recoded)
}
これにより、エスケープ処理が可逆的であることを保証しています。
期待される出力の変更
テストケースでは、以下の変更が期待されます:
// 修正前
"\x08\\9\\a\x0b\\c\\d\x0E\x0F"
// 修正後
"\x08\\9 \\a\x0b\\c \\d\x0E\x0F"
\9
と\c
の後に空白文字が追加されているのが確認できます。
関連リンク
- CSS Character Escapes - W3C
- CSS character escape sequences
- Go HTML Template Package Documentation
- CSS Escapes - Mathias Bynens