<前の日記(2005年04月24日) 次の日記(2005年05月20日)> 最新 編集

高木浩光@自宅の日記

目次 はじめに 連絡先:blog@takagi-hiromitsu.jp
訪問者数 本日: 49   昨日: 7469

2005年04月27日

クロスサイトリクエストフォージェリ(CSRF)の正しい対策方法

「クロスサイトリクエストフォージェリ」がにわかに注目を集めている。古く から存在したこの問題がなぜ今まであまり注目されてこなかったかについて考 えているところだが、引越しやら転勤やらでいまひとつ日記を書く時間がない。 しかし、 @ITの記事などのように混乱させる解説も散見されるので、一点だけ対策 方法について書いておくとする。

クロスサイトリクエストフォージェリ——Cross-Site Request Forgeries (CSRF)を防止する簡潔で自然な解決策は以下のとおりである。

前提

ログインしていないWeb閲覧者に対するCSRF攻撃(掲示板荒らしや、ユーザ登 録を他人にさせる等、サイト運営者に対する業務妨害行為)はここでは対象と しない。

ログイン機能を持つWebアプリケーションの場合、何らかの方法でセッション 追跡(セッション管理)を行っているはずであり、それをcookieの値を頼りに 行っている場合、そのcookieの値は第三者には予測不能な値が選ばれているは ずである。(でなければ、セッションハイジャックされたり、なりすましログ インされてしまうのだから、CSRF以前の重大な欠陥があることになる。)

最も自然な方式では、ログインごとに発行するランダムな受付番号、つまり 「セッションID」を1個(ないし https:// 専用を含む2個)用いて、どのユー ザからのアクセスなのかを検索して特定する——(A)。この値を十数桁以上の 乱数とすることで、偶然に的中する確率を何億分の1までに小さくしている。

あまり適切ではないが何らかの都合でセッションIDを使わずに、cookieにユー ザIDを入れる方式となっている場合もあるが、それだけでは予測不能とはなら ないので、そうした場合はそのユーザのパスワードのハッシュ値などもcookie に格納して、ページ毎に両方を確認することによって、なりすましされないセッ ション追跡を実現することになる——(B)。この場合の予測不能性は、パスワー ドが予測不能なはずであることに立脚している。

CSRFを防ぐ必要があるのは、Webアプリケーションに対して何らかの恒久的な データ変更を発生させるアクセスとなるページ(登録情報変更、設定変更、退 会処理、注文実行、取り消しなど)である。たとえば、情報を表示するだけの ページには対策が不要、もしくは重要性が低い(後述の「2ページ目」など)。 ショッピングカートへの商品追加などのように、一時的な状態変更を加えるだ けのページに対するCSRF対策の重要性は低めとなる*1

たとえば住所変更の機能を想定すると、1ページ目で現在の登録情報が入力欄 に埋め込まれて表示され、その入力欄の必要な部分を書き換えてボタンを押す と 2ページ目で確認画面となり、「変更」ボタンを押すと変更が処理されて 3 ページ目が表示されるという構成が多い。ここで、3ページ目に直接外部から ジャンプさせられると、住所を勝手に変更されてしまうことが起きる。

簡潔な対策方法

Webアプリケーションに対してデータ変更を処理させるページ(前述の「3ペー ジ目」)の前のページ(前述の「2ページ目」)に以下のHTML要素を含ませる。

 <input type="hidden" name="sessionid" value="セッション追跡用cookieの値"> 

そして、「3ページ目」で、そこに送信されてくるこの値が、cookieのその値 と一致しているかを調べて、一致しているときだけ処理を実行する。

「セッション追跡用cookieの値」には、前述の(A)方式ならばセッションIDの 値、(B)方式ならばユーザIDとパスワード(のハッシュ値など)の両方を格納 し、「3ページ目」でそれぞれの一致を確かめる。

第三者サイトから「3ページ目」へのハイパーリンク(JavaScriptによる自動 POSTを含む)が作られても、一致させる値を予測することは不能であるはずな ので、CSRF攻撃は成功しない。

制限事項

一致するかを確認するのは「3ページ目」だけでよいのか。そのままでは、 「2ページ目」へのハイパーリンクを作ることはできてしまう。しかしその場 合、被害者は「2ページ目」が現れたところで自発的に「3ページ目」へ進む ボタンを押さない限り被害に遭わない(XSS脆弱性など他の脆弱性がなければ)。

そこでボタンを押してしまいやすいかどうかは、「2ページ目」の画面内容に 依存する。たとえば、「本当に実行しますか?」以外に何も書かれていない 「2ページ目」である場合には、ボタンを押してしまうかもしれないので、 さらに前のページから同様の対策が必要となる。もっとも、ボタンを押すと何 が起きるのかを適切に説明しておくことは、もとより要求されるところである。

不適切な解説の例

  • GETを使わずPOSTを使え。*2
  • 実行の前に確認画面を挟め。*3
  • Referer:は偽装できるので対策にならない。*4
  • ワンタイムトークンを使わなくてはならない。*5
  • ログインなしのWebアプリケーションに対するCSRF攻撃(掲示板荒らし) を防止するには、セッションIDやワンタイムトークンを使えばよい。*6
  • 実行画面(「3ページ目」)に必要な情報を引数(hiddenを含む)に持た せず、「2ページ目」までにそれら必要な情報はセッション変数(Webアプリケー ション側で、セッションIDに結び付けて記憶している情報)に格納しておき、 「3ページ目」の処理ではそれを利用すればよい。*7

BASIC認証の場合

(続く)

*1 注文実行時にカー トの内容を確認するように作られているべきなので、そうなっていれば、気付 かないユーザの責任となる。

*2 JavaScriptで自動POSTさせられる。

*3 確認画面の次の実行画面に直接ジャ ンプさせられる。

*4 対策にならない理由 は、Referer:を送信しない設定にしているユーザがいるため。Referer:偽装が 問題となるのは、セッションハイジャック防止や、なりすましアクセス防止の ためにReferer:チェックをする話の場合。攻撃者が被害者の送信するReferer: を書き換えることはできないのだから、CSRFにReferer:偽装は関係ない。

*5 元々あるセッショ ン追跡用の秘密情報を使えばよい。

*6 Session Fixation攻撃によって回避される。

*7 「2ページ目」(引 数を持つ)に対してCSRF攻撃され、続いて「3ページ目」にCSRF攻撃される可 能性があるので、対策にならない。


<前の日記(2005年04月24日) 次の日記(2005年05月20日)> 最新 編集

最近のタイトル

2000|01|
2003|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|05|06|07|08|09|10|11|12|
2012|02|03|04|05|06|07|08|09|
2013|01|02|03|04|05|06|07|
2014|01|04|07|09|11|12|
2015|01|03|06|07|10|11|12|
2016|01|02|03|04|06|07|08|10|11|12|
2017|01|02|03|04|05|06|07|10|12|
2018|03|05|06|10|12|
2019|02|03|05|06|07|08|10|
2020|08|09|
2021|07|08|10|12|
2022|01|04|06|12|
2023|03|
2024|03|04|07|11|12|
2025|01|02|03|04|05|06|10|11|12|
<前の日記(2005年04月24日) 次の日記(2005年05月20日)> 最新 編集