はじめに

前回の記事の続きです。
前回の記事はこちら

クロスサイトスクリプティングの予防

前回は入力値を検証するバリデーション について勉強しました。
今回はXSS 対策についてです。

XSS対策については下記のように書いてありますね。

XSSに対する最も効果的な予防は以下の二種類を組み合わせることです:
すべての入力データを検証し攻撃の検査をすること(これに関しては前の節でいくつかご紹介しました)。
もうひとつは出力されるデータに対し適切な処理を行うことによって
すでに挿入されてしまったいかなるスクリプトに対してもブラウザで実行されないようにすることです。

要は <script></script> のようにブラウザが特別な意味として解釈可能な記述を無効化するような処理が必要ですね。
Goでは下記のような関数が用意されていると書いてありますね。

func HTMLEscape(w io.Writer, b []byte) //bに対してエスケープを行い、wに出力する。
func HTMLEscapeString(s string) string //sに対してエスケープを行い、結果の文字列を返す。
func HTMLEscaper(args …interface{}) string //複数の引数を同時にエスケープします。結果となる文字列を返します。

エスケープ処理をする場合について

まずは、エスケープ処理がない場合についてを試してみましょう。
<script>alert();</script>をユーザ名に入力してみます。
browser-01

Webサーバ側では下記のように出力されます。
web-server-01

エスケープ処理を追加してみます。
サーバサイドの処理を下記のように追加してみます。
fmt.Println("password:", r.Form["password"]) の下に追加しました。

1
2
3
fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) //サーバに出力されます。
fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
template.HTMLEscape(w, []byte(r.Form.Get("username"))) //クライアントに出力されます。

実行してみると下記のようになります。
web-server-02

<&lt;
>&gt; に変換されていますね。
longer thangrater than ですね。
このように小なり、大なりの記号が置換されていますね。

エスケープ処理しない場合について

Goのhtml/templateパッケージはデフォルトでhtmlタグをフィルターします。
しかし時にはこの<script>alert()</script>を正常な情報として出力したい場合があるかもしれません。
そのような場合はどのように処理するべきでしょうか?
この場合はtext/templateをご利用ください。

こういう場合もありますね。

下記のように記載すると良いらしいですね。

1
2
3
4
import "html/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", template.HTML("<script>alert('you have been pwned')</script>"))

下記のようになりますね。
web-server-03
エスケープされずに文字列として出力されています。

おわりに

日曜日は休みたいので、この時間に書きました。
明日は、ルーティンが終わったら一日中数学をやります。
sayonara