Home

技術日記
CSRFについてかみ砕いて説明してみるEdit

CSRFとか、わかりにくい言葉をわかりにくいままに使っているから、いまいちちゃんと知識が広まらないし、対策もひろまらないんじゃないか。

CSRFを「よそのサイトの機能を呼び出す攻撃」とかひらいて表記するようにすれば、もうちょっと雰囲気が伝わるんじゃなかろうか。

よそのサイトの機能ってのは、ごく普通のサイトだったら「メール送信フォーム」とか「掲示板」とか「コメント投稿」とか、そんなもののこと。

高機能なサイト、たとえばmixiとかFacebookとかのSNSなんかだと、友達登録とかメッセージとかいろんな機能がある。

そういう機能は、サーバー上で動作するプログラムによって動いているんだけど、サーバー上のプログラムに指示を出すのは、Webブラウザからのリクエスト。

Webブラウザからサーバーにリクエストを出す一番簡単な方法は、URLを呼び出すこと。

ブラウザのURL欄にごちゃごちゃ書いてリターンキーを押すと、そのURLによって指定されたサーバーに、リクエストが送られる。

一番よく使われるリクエストは、そのURLにあるコンテンツ(テキスト、画像、ムービー)をくれ、というリクエスト。普通にWebページを見るために使われる機能ね。

ごくごく単純な場合は、サーバーはリクエストされたURLに対応するサーバー上のファイル(テキスト、画像、ムービー)をそのまま返してくれる。

ただし、単にURLを呼び出すだけでも、サーバー上では何らかの特別な機能を実行する場合もある。

たとえば、

http://example.com/mail_send.php?subject=test&to=someone@example.com&body=hello

みたいなURLを開くだけで、「someone@example.com」というメールアドレスに、「test」というタイトルで本文が「hello」というメールを送信するプログラムなんてものもかんたんに作れる。

セキュリティってもの全く考慮せず、「自由に宛先とかタイトルとか本文とかを指定してメールを送るプログラムをサイト上に作れ」とか言われたら、そういうものができあがる。

もちろんそれに合わせて、subject、to、bodyを入力するフォームもサイト上には用意されるだろうけれども、入力フォームは、メール送信機能そのものではなく、メール送信機能に渡すパラメータを決めているだけだ。

入力フォームなんてものがなくても、上記のURLを開けばそれだけでメールは送信されるし、パラメータの内容はフォームがなくてもかんたんに変更できる。

そんなのがそこらに放置していると、上のURLの中のsubject、to、bodyのところを書き換えれば、自分の好きなところに好きな内容のメールが送れてしまうことになる。

じゃあ、そうやって自分が送りたいメールの内容に書き換えたURLを、試しにそこらの掲示板にでも貼り付けてみよう。「すごくおもしろいゲームを見つけたよ!」とかコメントを添えて。

そのURLをクリックする人がいれば、その数だけメールが送信されてしまうことになる。

もちろんそのURLを呼び出した=メール送信機能を実行したIPアドレスは、そのリンクをクリックした人間のものになる。

ただし、上記のようなURLが直接ブラウザで開かれた場合、その結果としてはメール送信プラグラムの実行結果ページが表示されるので、メール送信が実行されたことに気づくことができるだろう。

しかしそれをごまかす方法はいくつもある。

URLを開く方法として、普通の人が普通に知っているのは、リンクをクリックしたり、Webブラウザのアドレス欄にURLを書くことだろうけれども、それ以外にもいろいろな方法がある。

たとえば、HTMLを知っている人ならば、<img src="http://example.com/img/picture.jpg"&gt;というHTMLを書けば、http://example.com/img/picture.jpgに置かれている画像が表示されることは知っているだろう

これは、WebブラウザのURL欄に書くのと同様に、http://example.com/img/picture.jpgを呼び出している。

つまりHTML中に、<img src="http://example.com/mail_send.php?subject=test&amp;to=someone@example.com&amp;body=hello"&gt;と書けば、それが書かれたHTMLを開くと、自動的にメールが送信されることになる。

でも、そんな風に書いたら画像が呼び出されくて、画像が表示されないエラーが表示されるから、すぐにわかるよね、と思う人もいるかもしれない。

じゃあ、<img src="http://example.com/mail_send.php?subject=test&amp;to=someone@example.com&amp;body=hello" style="display:none;">とかかれていたらどうだろう。

最後の「display: none;」というのは「表示しないでね」という指定だ。これならば、画像読み込みのエラーが表示されることはない。

それ以外にも外部のURLを自動的に開くHTML表現はたくさんあるので、imgタグのところだけチェックすれば大丈夫だね、ということにはならない。

ところで、今まで説明してきたみたいに単純にURLを開くだけのことをGETリクエストという。

フォームからいろいろなパラメータを指定してサーバーに送信する際に、GETリクエストを利用すると、先ほどから書いているように「subject=test&to=someone@example.com&body=hello」という「項目名=値」を「&」でつないだようなURLの形でリクエストが行われる。

HTMLで書くと

<form action="http://example.com/mail_send.php"> タイトル <input type="text" name="subject"> 宛先 <input type="text" name="to"> 本文<textarea name="body"></textarea> <input type="submit" value="送信"> </form>

みたいな フォームを用意しておくと、上記のようなGETリクエストが送られることになる。

一方、URLにパラメータが含まれないPOSTリクエストを指定したフォーム送信の方法もある。上記のHTMLの一行目を、

<form action="http://example.com/mail_send.php" method="post">

に変えると、フォーム送信でアクセスされるURLは、

http://example.com/mail_send.php

のままになり、先ほどのような「subject=test&to=someone@example.com&body=hello」という部分はURLにはつかない。

じゃあパラメータはどうやって送られているのかというと、URLの一部ではなく、普通にWebブラウザを使っているだけの人間の目には触れない別の形で、サーバーに送られる。

サーバーでは、GETリクエストで送られたパラメータとPOSTリクエストで送られたパラメータは識別できる。

だから、http://example.com/mail_send.phpで動いているプログラムが、POSTリクエストでパラメータが送られた場合にしか動かないように作られていれば、単に

http://example.com/mail_send.php?subject=test&amp;to=someone@example.com&amp;body=hello

みたいなURLを呼び出しただけでは、メール送信機能が動いてしまうことはない。

じゃあ、サーバー上のプログラムを、POSTリクエストのみで動くようにしておけば、勝手に使われることはないんじゃないの?と思うかもしれないが、そうではない。

たとえば、

<form action="http://example.com/mail_send.php" method="post"> <input type="hidden" name="subject" value="test"> <input type="hidden" name="to" value="someone@example.com> <input type="hidden" name="body" value="hello"> <input type="submit" value="このボタンを押すと面白いよ!"> </form>

みたいなフォームをどこかの無料ホームページサービスなんかに置いておくとしよう。

上のフォームの「type="hidden"」というパラメータは、Webブラウザ上には見えない形でパラメータを指定する方法なので、上のフォームを表示すると「このボタンを押すと面白いよ!」というボタン一つが表示されるだけだ。

このボタンを見ただけで、いったい何が起こるのかがわかる人はいないだろう。

「何か面白いことが起こるのかも」と期待してボタンをクリックしたら、メールが送信されてしまう。

これだとユーザーがボタンを押さなければメールが送信されないので、ボタンを押さなければ安全なのね、と思うかもしれない。

しかし、それを回避して自動的にPOSTリクエストのフォームを送信する方法もある。

<script> var forms = document.getElementsByTagName('form'); forms[0].submit(); </script>

というJavaScriptは、HTML中に含まれるフォームの最初の一つを自動的に送信する機能を持つ。

つまり、フォームのHTMLと上記のようなJavaScriptを書いておけば、ページを開いただけで自動的にフォームが送信されてしまうことになる。

ただし、これはJavaScriptが有効な場合のみ働くので、WebブラウザでJavaScriptが動作しないように設定しておけば、この攻撃は回避できる。

一昔前までは、「信用できるサイト以外ではJavaScriptはオフにしよう」なんて使い方をすることで、こういう攻撃は回避できていたのだけれども、今時はもうJavaScriptが当たり前のように使われすぎているんで、そんな面倒な使い方は現実的ではないだろう。

しかし、このような方法でフォームを自動送信した場合は、Webブラウザはメール送信結果ページを表示するので、メールが送信されたことには気づくことができるかもしれない。

しかしそれをごまかす方法もある。

たとえば、フォームの1行目を、

<form action="http://example.com/mail_send.php" method="post" target="test">

のように「target="test"」を指定する。そして、

<iframe name="test" style="display:none;"></iframe>

のように見えない(display: none;)インラインフレーム(iframe)を用意しておく。

インラインフレームというのは、Webページ(HTML)中に別のWebページを埋め込むための仕組みだ。

自動的に送信されたフォームの結果は、インラインフレーム「test」に表示されるのだが、インラインフレーム「test」は表示しない設定になっているので、そのページを開いた人にはメール送信結果ページが見えることはない。

このような仕組みの罠は、HTMLという形で記述する必要があるので、単純に攻撃URLを掲示板に貼り付けることはできない。

こういう罠を仕掛けたHTMLをどこかのサーバー上に置いておき、そのURLを掲示板なんかに貼り付けるという2段階の仕組みが必要になる。

ちゃんとしているサーバ運営会社ならば、そういう危険なHTMLを仕掛けてもすぐに撤去されるし、罠を仕掛けた人間をアクセスログなどから追跡することにも協力的だろう。

というわけで、そういう罠が仕掛けられているページは、海外のあまり日本ではメジャーではないサーバーなどを利用して設定されていたりする。

じゃあ、知らないURLっぽいページは開かなければいいよね、と思うかもしれない。

しかし、今時は長いURLを別の短いURLに変換してくれるサービスなんかもあり、そういうものが使われていると、実際にクリックしてみるまで、どういうURLが開かれるのかはわからない。

また、実際にクリックしてみたところで、人間の目には実際にどういうURLが開かれたのかわからない場合もある。

Webブラウザは、リダイレクトという機能に対応している。リダイレクトというのは、指定されたURLから別のURLに自動的に移動する機能だ。

サーバーにリダイレクト先URLを指定した結果を返されたら、ブラウザは自動的にそのURLに移動する。リダイレクトは一瞬で行われるので、ユーザーの目にはリダイレクトされたかどうかは識別できない。

だから、ある短縮URLを開いた結果、たとえばhttp://www.yahoo.co.jp/が表示されたから、安全なURLだった、とは限らない。

たとえば、http://example.co.jp/too_dangerous_page/という罠が仕組まれたページに移動した後に、なにやら危険な処理を実行し、その後すぐにhttp://www.yahoo.co.jp/に移動したのかもしれない。

ユーザーの目に見えるレベルのURLなんてものは、かんたんにごまかすことができる。

これは偽装というレベルではなく、インターネットにおける当たり前の技術を当たり前に使っているだけのことだ。

というわけで、ここまで説明したいたちごっこで、ユーザーができる比較的安全な対処方法は、JavaScriptをオフにすることくらいなのだが、今となってはもうそういう訳にはいかないだろう。

ということで、ユーザー側が自衛できない以上は、サーバーサイドで対処しなければ、こういう攻撃は回避できない。

サーバーサイドでの対処策に関しては、技術者だけがわかればいい話なので、ここでかみ砕いて説明する必要はないだろう。方法論は確立しているので当たり前に対応すればいい。

セキュリティ的な危険性がある機能に対して回避策を講じていないのは単なる手抜きなので、非難されてしかるべきだろう。

ただ、認証不要のサイトにおける送信先固定のメールフォームに、CSRF対策が必須かというと、今回の事件が起きるまでは、「これはなりすましが可能ですよ」と関係者が理解していれば、CSRF対策をしていなくてもいいと思っていた。

が、今回の事件で「これはなりすましが可能ですよ」という理解を関係者に求めることは難しいように感じたので、今後は認証不要なサイトのメールフォームレベルの機能でも、CSRF対策は必要だろう。

また、このような知識はもうずいぶん前からインターネット技術者には当たり前のように知られていた内容なので、このような知識を前提とせずにインターネット犯罪を捜査しちゃまずい。

今回の事件は、単なるCSRFとウイルス経由の遠隔操作が混ざっているっぽいんだけど、前者は十分にあり得ることであると理解できていなかったとしたら、まずい。

その辺の情報がはっきりしないんだけど、どうなんだろう?

掲示板経由で命令を出すウイルスに関しては、botネットなんかではよく使われていると言われている手法だから、インターネット犯罪を捜査する人間ならば、方法的には知っていてしかるべきではある。

けれども、この辺はセキュリティを本格的に研究している人しか、具体的な情報に触れることはないかもしれないから、詳しく知らなくてもしょうがないかもしれない。

というか、私もそういうものらしいと噂でしか聞いていなかったから、実際にこういう事件があったときにそういう可能性を主要な選択肢としては思いつかなかったかもなー。

なんかものすごく長くなった。かみ砕いて説明したつもりだったけど、あんまりかみ砕けてないかもしれない。

[宣伝] Twitterベースの掲示板システム開発中! http://tweet-bbs.com/

Published At2012-10-23 20:23Updated At2012-10-23 20:23