Arantium Maestum

プログラミング、囲碁、読書の話題

Clojure Web Development勉強 - Hiccup (その2) XSSの恐怖

なにやら五島勉のようなタイトルをつけて恐縮だが、Hiccupでも良く指摘される重大な問題として、自動的に文字列をエスケープしないことによるXSS脆弱性の導入がある。

クロスサイトスクリプティング - Wikipedia

つまり、今まで紹介してきたコードにもあるように、「フォームなどでユーザインプットを受け取りそれをHTMLに埋め込んで返す」というような挙動の場合、そのインプットに任意のJavaScriptコードなどが入っていると、それをユーザのブラウザ上で自動的に走らせてしまう恐れがある。恐ろしいのは「あるユーザの入力したスクリプトが別のユーザのブラウザで走る場合」そして「そのスクリプトが与えられている権限が、サイト全体の権限と同一で、一般的な権限よりも大きい場合」である。

それを避けるために「文字列をエスケープする」、つまり「タグやコードとして認識されるような部分を無理やり出力するべき文字として認識するようエスケープ記号などを挿入する」ことが重要になる。

そういえばこの話題はJoel on Softwareでも言及されてたな。

www.joelonsoftware.com

Let’s put it in pseudocode. Imagine that

s = Request("name")

reads input (a POST argument) from the HTML form. If you ever write this code:

Write "Hello, " & Request("name")

your site is already vulnerable to XSS attacks. That’s all it takes.

ということで、今までのHTMLベタ打ちなコードではこの脆弱性が存在していた。

前述の通りHiccupは自動的には文字列をエスケープしてくれない。

(html [:div "<script>alert()</script>"])

のようなコードは

"<div><script>alert()<script></div>"

と変換されてしまう。

ただしくエスケープするためには、Hiccupが提供しているh関数で文字列を変換してやる必要がある。例えば

(html [:div (h "<script>alert()</script>")])

だと結果は

"<div>&lt;script&gt;alert()&lt;/script&gt;</div>"

で、ブラウザでもちゃんと文字列として表示されるようになる。

前回のコードで動的にユーザインプットを表示していたのはviews.cljのdisplay-result関数である。GET/POSTフォームのパラメータからnameを取り出してHTMLに埋め込んでいた。

(defn display-result [req]
  (let [{:keys [params uri]} req
        param-name (get params "name")
        req-type (if (= uri "/get-submit") "GET" "POST")]
   (html
    [:div
     [:h1 "Hello " (h param-name) "!"]
     [:p "Submitted via a " req-type " request."]
     [:p [:a {:href ".."} "Return to main page"]]])))

ちゃんと(h param-name)となっているので、例えば入力された値が</h1><script>"Hello XSS!"</script><h1>などというJavaScriptが含まれたものでも、ちゃんとウェブページ上で

Hello </h1><script>alert("Hello XSS!")</script><h1>!

と表示される。

ちなみに(h param-name)param-nameにして再実行すると、Chromeの場合JavaScriptは実行されず、Console Logでこんなメッセージが出る:

The XSS Auditor refused to execute a script in 'http://localhost:8080/post-submit' because its source code was found within the request. The auditor was enabled as the server sent neither an 'X-XSS-Protection' nor 'Content-Security-Policy' header.

ダンブラウザは偉大である。が、とりあえずモダンじゃないブラウザもまだ稼働してそうなことだし、やはりXSS対策は開発者側のほうでできる限り対応するべきだろう。ということでh関数はユーザインプットを受け取るところではいたるところで使うべきだろう。

最後に参考文献的なものを。

Clojure Web Security全般についてはこの発表が面白かった。

www.youtube.com

このどちらかというと問題山積みな状況から二年半でClojureのセキュリティ関連のエコシステムがどう変わったのか、とても興味がある。Web開発の勉強を進めていく上で気をつけたいポイントである。

XSSそのものではないが、似たようなセキュリティ問題について:

http://imgs.xkcd.com/comics/exploits_of_a_mom.png

セキュリティは大きな問題なので体系的かつ早期に学ぶことが望ましい(多分)。というわけでずっと積ん読していたこれをぼちぼち読んでいる。

www.amazon.co.jp

いろんな脆弱性があるなぁ・・・(遠い目 ウェブ開発は大変である。

追記: このエントリ書いていて知ったんだけど、Hatena BlogってユーザインプットからJavaScript実行するのな・・・ Preview機能を使ったら突然Alertポップアップが開いて驚いた。(修正済み)