セキュリティー

Jeans CMSにおけるセキュリティーの確保

2010年1月5日

今のところ、セキュリティーの確保として、次のような攻撃について対処しています。

1)ヌルバイト攻撃
2)SQLインジェクション
3)XSS
3)リモートファイルインクルードとディレクトリトラバーサル
5)CSRF
6)セッションハイジャック

ヌルバイト攻撃の対策のために、Jeans CMSでは簡易ファイアウォールを構築しています。通常、$_GET, $_POSTなどの外部から来る情報にヌルバイトが含まれている場合、自動的にコードの実行を停止します。バイナリファイルを受け渡ししたい場合(そういう事例はほとんど無いと思いますが)、$_POST['file_bin']のように、接尾辞として『_bin』をつけるようにします。このファイアウォールはSQLインジェクションやXSSなどへの対策もかねていて、例えば$_GET['temid']に『<』『>』『”』『’』などの記号が含まれている場合も、実行を停止します。これらの記号を含めるには、$_POST['body_text']などのように、『_text』を接尾辞として追加するようにします。

SQLインジェクションの対策は、比較的容易です。Jeansでは、sql::query()メソッドでプリペアードステートメントを利用することで、対策を行っています。例えば、『SELECT * FROM jeans_item WHERE id=<%id%>』のようにクエリーを記述し、『array('id'=>$_GET['itemid']);』のように外部データを与えます。この記述で、PDOの持つプリペアードステートメント機能が利用されますので、それによりSQLインジェクションが排除できます。万一SQLインジェクションを許してしまった場合に、被害を最小限に抑えるための工夫もしています。jeans_loginやjeans_ticketなど、ユーザーの権限を扱うテーブル用のデータベースファイルと、一般のテーブル用のデータベースファイルを分けてあるのがそれです。一般のテーブルを扱うSQLクエリーで仮にインジェクションが起きてしまっても、権限の奪取やセッションハイジャックなどが起こりにくい設計です。

XSS対策はもう少し複雑です。Jeansではいくつかの対策を行っています。まず、規約として、コード中で『echo』『print』などの生のデータを出力する命令の記述を禁止しました。このような規約は文書として、後ほどまとめたいと思います。変わりに、jeans::echo_html()メソッドを用意し、『self::echo_html()』のように記述することで同様のことが行えるようにしています。『echo』の代わりに『self::echo_html()』を利用することで、次のようなことを狙っています。

 ・コードを書くときに、htmlを出力しようとしていることを、自覚できる
 ・外部からのデータは、メソッドの2番目の引数として与えることで、サニタイズされる
 ・UTF-8エンコードに従わない内容は出力されない

もうひとつ、『self::p()』というメソッドも使えます。これは、与えたデータをサニタイズして表示します。これらのメソッドにおけるサニタイズでは、デフォルトでは『”』と『’』を『`』に変換し、HTMLタグを取り除いた後に(strip_tags)、htmlspecialcharsで処理をしています。これでほぼ100%、XSSが排除できると考えます。スキン中に頻繁に現れる<%data%>というスキン変数でも同様の処理をしており、これもXSS対策の一環です。こちらの方はデフォルトのサニタイズでは困る場合、<%data.hsc%>や<%data.raw%>などのスキン変数を使うことになりますが、これらのスキン変数を用いる場合のみ最新の注意を払うようにすることで、より少ない労力でセキュリティーの確保ができるように考えたつもりです。

リモートファイルインクルードとディレクトリトラバーサルへの対処として、『include()』系の4つの命令を使うことを規約で禁止しています。代わりに、『self::include_local()』メソッドを用いることで、これら2つの攻撃に対して対処しています。同様に、『file_exists()』の代わりに『self::local_file_exists()』を、『get_file_contents()』などの代わりに『self::local_file_contents()』を利用するのが規約です。もうひとつの対策として、PHP5の__autoload()関数の活用が挙げられます。Jeans では、『include()』系の命令を用いる必要はほとんどありません。必要なコードのインクルードとパースは、Housekeeping Jeansが自動的に行います。ですので、『self::include_local()』メソッドすら、めったに使うことは無いと思います。

CSRF対策として、データベースの書き換えなどを行うアクションは、HTTPのPOSTメソッドで行うことを規約としました。この場合、有効なチケットが必要です。チケットはユーザー別・アクション別に発行されるので、攻撃を行う側がサイトの乗っ取りに必要なチケットを奪取することが、Nucleusよりも難しくなっています。

セッションハイジャックへの対策は上に挙げたものも関連しますが、それとは別のものとして次のような事が挙げられます。まず、これは最新版のNucleusと同じですが、セッション継続用のクッキーデーターには、接続する側のIP情報が含まれています。従って、仮にXSSでクッキーが漏れても、同じ(もしくは類似の)IPアドレスから接続しない限り、ハイジャックは起きません。仮にIP詐称で不正に接続したとしても、IP詐称では有効なチケットを得ることができないのでハイジャックは起きません。これは、SQLインジェクションでセッション継続用クッキーのデーターが漏れたケースでも同様です。SQLインジェクションで新規ログイン用のhash値がもれた場合の対処としては、2つ挙げられます。ひとつは、hash値の作成にMD5ではなくSHA512を用いていること。もうひとつは、saltを用いてhash値を作成していることです。saltはconfig.phpで与えられるので、攻撃例としては次のようなことが必要になります。

 ・SQLインジェクションにより、hash値を奪取
 ・ディレクトリトラバーサルなどにより、config.phpの内容を閲覧
 ・ブルートフォースにより、パスワードを割り出す

現在のところ、これら3つの事項すべてにおいて、強力な防御をしてあるつもりです。ただ、サイトへ直接アクセスすることによるブルートフォース攻撃対策はまだです。これは、ベータバージョンを出すまでの課題にしたいと思います。

もうひとつ、これはこの手のwebアプリケーションに共通のことですが、中間者攻撃に対処するにはSSLの利用(https)が必須です。管理画面でhttpsを用いることができるように、コードを管理してゆきたいと考えています。

その他、セキュリティーに関してこんなことも必要だよといったことがあれば、コメントくだされば嬉しいです。

コメント

Kat (2010年1月7日 06:13:06)

http://kandk.cafe.coocan.jp/blog/jeans/index.php?itemid=651
上記のページで述べたことで、まだ対策されていないものを挙げておきます。

1)jeans::local_file_exists()における、ヌルバイトチェック
2)ファイルアップロード:拡張子のチェックをホワイトリストで行うこと。拡張子がイメージの場合、getimagesize()でチェックする。
3)<a href="xxxx">のxxxxの部分に挿入するためのサニタイジングメソッド。<%data.url%>???

Kat (2010年2月15日 06:32:50)

PHPのheader()関数をラップして、改行コードを含む文字列を受け付けないようにしようかと思いましたが、これはすでに、PHP 5.1.2以降では解決済みのようです。Jeansでは、5.2以上が必要なので、問題ありません。

Kat (2010年2月15日 07:25:25)

1月7日の3つの宿題、すべて実装。2については、イメージをDBに保存することになった関係で、当初の予定よりもセキュリティーはより高くなった。

Kat (2010年3月9日 04:15:55)

サイトへ直接アクセスすることによるブルートフォースアタック対策は、実装済み。間違ったパスワードを入力すると、一定時間、同じIPからのログインが無効になる。一回の間違いで1秒、2回で3秒、3回で7秒、4回で15秒。16回で65535秒無効。これ以上の間違いは、24時間(86400秒)の無効。

コメント送信