JavaScriptセキュリティの基礎知識

第3回Webセキュリティのおさらい その3 CSRF・オープンリダイレクト・クリックジャッキング

前回は、Webアプリケーションにおける受動的攻撃の代表例の1つであるXSSについて、原理や対策を振り返りました。今回は、同じく受動的攻撃の代表例であるCSRF、オープンリダイレクト、クリックジャッキングについて掘り下げて解説していきます。

CSRF(クロスサイトリクエストフォージェリ)

CSRFはどのように引き起こされるのか

CSRFとは、たとえば掲示板の書き込みや設定情報の変更などの機能に対して、攻撃者のサイト上に設置されたフォームなどから強制的にリクエストを発行することで、ユーザーの意図していない操作と同様の結果をもたらす攻撃手法です。Webアプリケーションに永続的な副作用がある機能が攻撃の対象となります。

たとえば、http://example.jp/上に設置された掲示板で以下のようなHTMLがあったとします。

<form method="POST" action="/board">
    <label for="username">お名前</label>
    <input type="text" id="username" name="username">
    <label for="message">メッセージ</label>
    <textarea id="message" name="message"></textarea>
    <input type="submit" value="書き込み">
</form>

このフォームを用いて、⁠お名前」欄に「田中⁠⁠、⁠メッセージ」欄に「こんにちは。」を入力した状態で「書き込み」ボタンを押し、掲示板へテキストを書き込むときのブラウザからWebサーバへのHTTPリクエストは以下のようなものになります。

POST /board HTTP/1.1
Host: example.jp
Content-Type: application/x-www-form-urlencoded

username=田中&message=こんにちは。

本来は、これら以外にも多くのHTTPリクエストヘッダが付与されますが、ここでは説明をかんたんにするために、必要最小限のヘッダのみを示しています。また、リクエストボディ中のusernameおよびmessageパラメータは、本来はURLエンコードされ`username=%E7%94%
B0%E4%B8%AD&message=%E3%81%93%E3%82%93%E3%81
%AB%E3%81%A1%E3%81%AF%E3%80%82`のようになりますが、これも説明をかんたんにするために、そのまま日本語のテキストで書き示しています。

サーバ側は、このようなリクエストを受け取ると、usernameおよびmessageのパラメータをそれぞれ掲示板の内容として内部のデータベースに書き込みます。

このような掲示板に対して、攻撃者が自身の罠サイトであるhttp://attacker.example.com/trap.html上に、以下のようなHTMLを用意したとします。

<body onload="document.forms[0].submit()">
       <form method="POST" action="http://example.jp/board">
       <input type="text" name="username" value="名無しさん">
       <textarea name="message">これは犯罪の予告です。XX月XX日、YYYにて大規模な犯罪を行います。</textarea>
   </form>
</body>

フォーム自体は攻撃者のサイトであるhttp://attacker.example.com上にありますが、フォームの送信先が掲示板の設置されているhttp://example.jp/boardになっています。

攻撃者は、このような罠ページを用意したのち、SNSやメール、ほかの掲示板などを経由してhttp://attacker.example.com/trap.htmlというURLを第三者に伝え、だれかがこのURLを訪問するのを待ちかまえます。

ユーザーがうっかりこのURLをクリックして攻撃者の用意した罠サイトを訪問すると、ページを開いた瞬間にJavaScriptが動作し、以下のようなHTTPリクエストがユーザーのブラウザから掲示板の設置されているhttp://example.jpに対して発行され、攻撃者の用意した内容のメッセージ、すなわち犯罪予告がexample.jp上の掲示板に書き込まれてしまいます。

POST http://example.jp/board HTTP/1.1
Host: example.jp
Content-Type: application/x-www-form-urlencoded

username=名無しさん&message=これは犯罪の予告です。XX月XX日、YYYにて大規模な犯罪を行います。

掲示板への書き込みのリクエストは、攻撃者ではなく、罠ページを閲覧したユーザーのブラウザから行われますので、掲示板上で書き込みのIPアドレスを記録している場合にはユーザーのIPアドレスが記録されます。そのため、掲示板への犯罪予告の書き込みに対して警察などによる捜査が行われた場合には、攻撃者ではなく、罠ページを閲覧したユーザーが捜査対象となってしまうこともありえます。

このように、Webアプリケーションにおいて副作用の発生するリクエストを、罠ページなどを経由してユーザー自身に発生させる攻撃手法がCSRFです。

ログインを利用してメールアドレスを強制的に変更してしまう仕組み

では次に、CSRFの対象となるWebアプリケーションが掲示板ではなくログイン機構を持つものであった場合を考えてみましょう。

たとえば、ログイン後の個人設定の変更においてメールアドレスを変更するためのフォームがあったとします。

<form method="POST" action="/changeaddr">
    <label for="email">メールアドレス</label>
    <input type="email" id="email" name="email" value="yourmail@example.jp">
    <input type="submit" value="メールアドレスを変更する">
</form>

このフォームを用いて、メールアドレスとしてnewaddr@example.jpを設定した場合のブラウザからWebサーバへのHTTPリクエストは以下のようなものになります。

POST /changeaddr HTTP/1.1
Host: example.jp
Cookie: sessionid=25283FB24C9DEE32
Content-Type: application/x-www-form-urlencoded

email=newaddr@example.jp

すでにWebアプリケーションにはログイン済みであるため、ログイン後のセッションIDであるCookieがリクエスト内のCookieリクエストヘッダとして送信されています。

サーバ側は、このようなリクエストを受け取ると、データベース内に登録されているユーザーのメールアドレスをnewaddr@example.jpに変更して書き込みます。

このような設定画面を持つWebアプリケーションに対して、攻撃者は自身の罠サイトであるhttp://attacker.example.com/trap.html上に今度は以下のようなHTMLを用意します。

<body onload="document.forms[ 0 ].submit()">
   <form method="POST" action="http://example.jp/changeaddr">
       <input type="email" name="email" value="hijack@attacker.example.com">
   </form>
</body>

攻撃者はこのような罠ページを用意したのち、先ほどの掲示板のときと同様に、http://attacker.example.com/trap.htmlというURLをSNSやメール、ほかの掲示板などを経由してほかのユーザーに伝え、そのユーザーが罠URLを訪れるのを待ちかまえます。

すでにログイン済みのユーザーがこのURLを訪問すると、以下のようなHTTPリクエストがユーザー自身のブラウザからWebアプリケーションの設定画面に対して発行されます。

POST http://example.jp/changeaddr HTTP/1.1
Host: example.jp
Cookie: sessionid=25283FB24C9DEE32
Content-Type: application/x-www-form-urlencoded

email=hijack@attacker.example.com

ユーザー自身はWebアプリケーションにすでにログイン済みですので、Webアプリケーションに対してのリクエストにはセッションIDを含むCookieがそのまま付与されます。結果として、ユーザー自身は意図していないにも関わらず、メールアドレスが攻撃者のものに強制的に変更されてしまうことになります。

このように、攻撃者の用意した罠ページを起点とし、ユーザー自身に攻撃対象となるサイトへのリクエストを発行させることによって、ユーザー自身が意図していない処理を行わせるのがCSRFです。

CSRFへの対策

CSRFへの対策としては、攻撃者が知りえない値をリクエスト内に埋め込む、すなわち

  • フォーム内にhiddenを用いてトークンを埋め込む
  • サーバ側でリクエストを受け取ったときに、そのトークンを確認する

という方法がもっとも広く使われています。たとえば以下の例では、tokenという名前でhiddenが指定されたinputタグに、攻撃者には推測できないランダム値を埋め込んでいます。

<form method="POST" action="/changeaddr">
    <label for="email">メールアドレス</label>
    <input type="email" id="email" name="email" value="yourmail@example.jp">
    <input type="hidden" name="token" value="C2A8DDEF926432BD">
    <input type="submit" value="メールアドレスを変更する">
</form>
<form>

サーバ側では、formのPOSTリクエストを受け取ったときに、tokenの値が正しいものか検証することで、リクエストが正規の画面遷移によって発行されたものであることを確認できます。

トークンを利用する方法以外にも、パスワードを再入力させる方法や、CAPTCHAなどを利用する方法もありますが、本稿では説明は省略します。

オープンリダイレクト

オープンリダイレクトはどのように引き起こされるのか

Webアプリケーション内に、指定されたほかのページへリダイレクトする機能がある場合に、その機能を利用して、攻撃者が指定した任意のURLへリダイレクトが可能になってしまう脆弱性をオープンリダイレクトといいます。

たとえば、http://example.jp上で動作するWebアプリケーションにおいて、http://example.jp/go?url=/next.htmlというURLにアクセスするとhttp://example.jp/next.htmlへリダイレクトする機能があったとします。

GET /go?url=/next.html
Host: example.jp

HTTP/1.1 302 Moved Temporarily
Location: /next.html
Content-Length: 0

このような同一サイト内でのほかのページへのリダイレクトを想定して作られている機能において、http://example.jp/go?url=http://attacker.example.com/evil.htmlのようにhttp://example.jpとは異なるサイトのURLを指定したときに、そのサイトにリダイレクトしてしまうのがオープンリダイレクトです。

GET /go?url=http://attacker.example.com/evil.html
Host: example.jp

HTTP/1.1 302 Moved Temporarily
Location: http://attacker.example.com/evil.html
Content-Length: 0

オープンリダイレクトによって、攻撃者はURLをそのサイト内の一部だと見せかけてユーザーを悪意あるサイトへ誘導したり、検索エンジンでの表示順位を汚染するSEOポイズニングなどに悪用することがあります。XSSやCSRFとは違って、Webサイトそのものやユーザーに直接的な被害が及ぶわけではありませんが、URLとして表示されているドメインを信頼して訪問したにも関わらずほかのサイトへリダイレクトされてしまうのは、サイトやドメインに対する信頼を損ねることにもつながります。

オープンリダイレクトへの対策

オープンリダイレクトへの対策としては、リダイレクト先のURLをあらかじめWebアプリケーション内で固定のリストとして保持しておき、それ以外のサイトへのリダイレクトを発生させないというのが最も堅実な方法となります。例として、以下のPHPコードを見てみましょう。

$targets = array( 
    'next' => '/next.html',
    'login' => '/login.html'
);
$nexturl = $_GET['url'];
if (isset($targets[$nexturl])) {
     header('Locatoin: ' . $targets[$nexturl]);
}else{
     header('Locatoin: /');
}

このPHPのコードでは、

  • http://example.jp/go?url=nextというURLにアクセスされたときは、http://example.jp/next.htmlへ
  • http://example.jp/go?url=loginというURLにアクセスされたときは、http://example.jp/login.htmlへ
  • それ以外の場合は、http://example.jp/へ

とリダイレクトされることになり、攻撃者によってリダイレクト先をコントロールされる余地がなく、オープンリダイレクトとして悪用されることもありません。

クリックジャッキング

クリックジャッキングはどのように引き起こされるのか

クリックジャッキングとは、攻撃対象となるWebサイトを透明にした状態で、iframeなどを用いて攻撃者の用意した罠サイト上に設置し、罠サイト上でユーザーにクリックなどの操作の錯誤を起こさせる攻撃手法です。

罠サイトでは、訪問してきたユーザーがうっかりクリックしたくなるようなコンテンツが表示されていますが、そのコンテンツには攻撃対象となるWebサイトが透明になった状態で、iframeなどで重なって配置されています。そのため、ユーザーが罠サイト上のコンテンツをクリックすると、実際にはiframe内のWebサイトに対してクリックが発生してしまいます。iframe内のWebサイトでは、クリックすることで副作用が発生するような機能のボタンなどを配置しておくことで、ユーザーのクリックによってCSRF同様にユーザーに強制的に操作を行わせることができてしまいます。

クリックジャッキング
クリックジャッキング

クリックジャッキングへの対策

Webアプリケーションとしてクリックジャッキングへの対策を行うには、該当ページをframeやiframe内に表示されないようにする必要があります。そのために使われるのが、X-Frame-Options HTTPレスポンスヘッダです。X-Frame-Optionsレスポンスヘッダが指定されたリソースは、ブラウザ上でframeやiframeでの表示が禁止されます。

X-Frame-Optionsでは、以下の3通りの指定が可能です。

X-Frame-Options: DENY
→該当ページのframeやiframeへの埋め込みを一切禁止する
X-Frame-Options: SAMEORIGIN
→該当ページのframeやiframeへの埋め込みは、同一オリジンからの場合のみ許可される
X-Frame-Options: ALLOW-FROM http://example.com
→該当ページのframeやiframeへの埋め込みは、指定されたオリジンからの場合のみ許可される

DENYを指定した場合には、自サイトからであっても、フレーム内にそのコンテンツを読み込めなくなります。

Webセキュリティのおさらい まとめ

本連載の第1回から第3回までで、Webセキュリティのおさらいとして、攻撃には能動的攻撃と受動的攻撃があり、受動的攻撃としてはXSS、CSRF、オープンリダイレクト、クリックジャッキングがあることを説明しました。

CSRFやクリックジャッキングなどは、Webアプリケーション自身の問題というよりは、Webの仕組みそのものが持つ問題がWebが成熟するとともに顕在化してきたともいえるかもしれません。また、オープンリダイレクトのように、Webアプリケーション側の脆弱性なのか素直に判断しにくい問題もあります。

このような問題についてもWebアプリケーション側で対策が迫られるのは、Webというものがより日常生活に密着し、高い信頼性が求められるようになってきたからでもあります。

おすすめ記事

記事・ニュース一覧