symfonyフレームワークのセキュリティ

このblogでは、主にjobeet*1という、symfony*2チュートリアルに沿ってsymfony自体や、PHPとその周辺について勉強を進めていく中で、気になったこと、調べたことをまとめていこうと思います。

途中まで進めているので、しばらくはメモを見て思い出しながら書いていこうと思います。



今回はsymfonyでのセキュリティについて。

webアプリを作る上で、セキュリティは切っても切れない存在だと思います。
アプリケーションフレームワークに触れるのは始めてだったのですが、jobeetを始める前から

  • セキュリティの知識はもちろん必須
  • 各脆弱性について対策が枯れてるなら、フレームワーク側で対処してくれればいいのに
  • でもフレームワークの方でやってくれたら、セキュリティに対する意識が薄くなるかも

とぼんやり考えてました。
そんな感じでjobeetをはじめたわけです。


symfonyでは、デフォルトでXSSとCSRF対策をしてくれます。*3
1日目に書いてあって、関心。いわくjobeetは

「実際のアプリケーションでは、バリデーションと適切なエラーハンドリングを追加するのを忘れないでください」

ã‚„

「読者の練習のため、セキュリティについては考慮していません」

ã‚„

「もちろんテストの記述は必要になります」

なんてことは言わない、実用的なチュートリアルなんです。

XSSって?

XSS(クロスサイトスクリプティング)*4とは、
ウェブアプリへの入力をきちんとエスケープしないでHTMLに出力してしまうことで、入力に含まれているタグがそのままHTMLとして解釈されてしまうという脆弱性。

簡単な例として、

<div> 名前 : <?php echo $_POST['user'] ?></div>

という風にPOSTの変数を直接出力してしまうと、

<script>while(1){ alert( 'test' ); }</script>

のようにJavascriptが入力された時にそのまま実行されて、大変なことになります。

こんな感じで、任意のスクリプトが実行できてしまうので、XSSを利用した攻撃はわりとこわい。
wikiによると、XSSを利用することで

  • クッキーの値を取得あるいは設定することにより、セッションハイジャックする
  • 強制的なページ遷移を起こさせ、クロスサイトリクエストフォージェリ対策を回避する(取得したクッキーを攻撃者側でそのまま利用できない場合に用いられる手法)
  • ページ全体を置き換えることにより、偽のページを作り出す(典型的にはフィッシングに用いられる)
  • フォームの送信先を置換することにより、入力を第三者サイトに送信するよう仕向ける

といった攻撃が成立するらしい。

対策

XSSの対策は、出力値のエスケープを適切に施すことらしいです。
(らしいですというのは、自分がまだ「適切に」というのを把握してきれてないから)
wikiによると、

  • HTMLの実体参照を用い、& ã‚’ & に、< ã‚’ < に、> ã‚’ > に、" ã‚’ " に、それぞれ置換する。(サニタイジング*5)
  • タグの属性値は必ず "〜" (ダブルクオート)で括る。また属性値中のエスケープを忘れない。例:

だし、PHPとWebアプリケーションのセキュリティについてのメモ*6によると、

htmlspecialchars()を通して出力すれば、ほとんどのXSSは回避できます。

とのこと。ほとんど、ってなに?って感じだけれど。
この辺は全体を把握できるようにならんといかんですね。

じゃあ、symfonyはなにをしてくれるの?

symfonyのescape処理については、こんなページを見つけました。
symfony のエスケープ処理 - アシアルブログ

出力値のescapingの設定は、apps/frontend/config/settings.yml をいじることでできます。
escaping_methodでescapeの方法を指定します。デフォルトではESC_SPECIALCHARSに設定されてました。
それぞれの設定は

  • ESC_RAW: 値のエスケープを行わない
  • ESC_SPECIALCHARS: 入力に対して、htmlspecialchars()でエスケープを行う
  • ESC_ENTITIES: ENT_QUOTES引数付き*7のhtmlentities()でエスケープを行う
  • ESC_JS: HTMLとして使用されるであろうJavaScript文字列をエスケープする。
  • ESC_JS_NO_ENTITIES: JavaScritp文字列をエスケープするが、エンティティは付加しない。

記事が少し古いのが気になりますが、エスケープに使われているhtmlspecialchars()とhtmlentities()の違いについては以下のページがわかりやすいかと。
http://d.hatena.ne.jp/teracc/20070415

肝心のエスケープはlib/vendor/symfony/lib/helper/EscapingHelper.phpで定義された関数で行われていて、escaping_methodにESC_SPECIALCHARSが指定されている場合は、55行目の

function esc_specialchars($value)
{
  // Numbers and boolean values get turned into strings which can cause problems
  // with type comparisons (e.g. === or is_int() etc).
  return is_string($value) ? htmlspecialchars($value, ENT_QUOTES, sfConfig::get('sf_charset')) : $value;
}

で行われてるっぽい。ちゃんとhtmlspecialchars()が使われてますね。

で、これがどこで呼び出されてるかまではコード追えませんでした。

あとで、もう少し詳しくsymfonyのコードを追って見ようと思います。

  • viewのコード
  • lib/vendor/symfony/lib/escaper/sfOutputEscaper*.class.php

ちら見した感じだと、このあたりを読めばescapingの流れがつかめるかなと予想。


ちなみにtemplateで利用できる変数$sf_dataはsfOutputEscaperArrayDecoratorクラスのインスタンスみたいですね。

疑問

これって、入力したユーザ自身が攻撃の被害を受けるっぽいけど、自分で自分を攻撃するなんてことはないはず。
だとしたら、実際の攻撃が行われるシチュエーションってどんな感じなんだろう?

ぱっと思いついたのは、該当箇所がGETの値を使ってるなら、うしろにデータのついたURLを踏ませれば、自分以外の人をターゲットにできるかなー、といった感じ。



意外とXSSについて調べるだけで時間がかかってしまったので、CSRFについてはあとでまとめようと思います。


なんとなくまとめてみて、

フレームワークに任せっきりじゃなくて、全体を把握した上で、フレームワークでなに対策を”どういうふうに”してくれてて、何はしてないのかをきちんと把握する

のが大事っぽいな、という結論に。


まだまだセキュリティに関する知識は足りないのでアレですが、ぼちぼち勉強していかないとなぁ。