はじめに

Yesod は Haskell で書かれた Web アプリケーションフレームワークです。

WordPress や Drupal と違って、セキュリティはかなり万全です。(ユーザが気にしなければならない部分が非常に少ないです)

最近、体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 (通称: 徳丸本) が発売されました。

勉強のため、本書の内容を Yesod で確認しているのですが、その中で面白い例を見つけました。

今回はその内容について紹介したいと思います。(セキュリティの専門家ではないので間違いがあればご指摘ください)

脆弱性のあるコード

このコードには脆弱性があります。

Yesod の変数展開 #{..} は、このような JavaScript の動的生成でも大丈夫だろうと思っていたのですが、そうではありませんでした。

正常系

期待する動作として、例えば http://localhost/?name=bigmoon いう形式の URL にアクセスすると、以下のような URL が組み立てられます。

これは予定通りです。

異常系

URL の形式を http://localhost/?name=%27),alert(XSS)// とするとインジェクションが発生します。

読みやすさのため、パーセントエンコーディングを元に戻します。

alert が出てきてしまいましたね・・・。

余談ですが Yesod ではパラメータ中に出現する ; をパラメータの区切り文字として認識するようです。そのため http://localhost/?name=%27);alert(XSS)// ではインジェクションは発生しません。

原因

問題はどこにあるのでしょうか?

パラメータを取得しているコードはこの部分です。

lookupGetParam の型は以下の通りです。

つまり、mname :: Maybe Text 型となってしまいます。ここが問題の原因です。

ただの Text 型なので変数展開時に HTMLのエスケープ処理 が行われます。本来ならば JavaScript 用のエスケープ処理が必要なのです。

展開部分を改良してみる

では Javascript 型に変換すれば問題は解決するのでしょうか?

試しに以下のようにコードを変更してみました。

ですが、やはり結果は同じです。

エスケープ処理

ソースコードを追いかけてみるとどうやら Value の値については string というサニタイザーが適用されるようです。

なので、先程のコードを少し修正してこの string を適用してみました。

しかし、これでもやっぱりだめです。なぜなら ' に対してはサニタイズしていないからだと思います。

string 関数の修正

以下のように ' のエスケープ処理を追加したところ、インジェクションが発生しなくなりました。

まとめ

  • JavaScript を動的に組み立てない
  • ユーザが気をつけなけば Yesod でも脆弱性が発生する
  • 変数展開で全てがエスケープされると思っていたが、場所によってはそれだけでは不十分な場合がある

以上です。