ヘソクリの在処と金額を管理しているサービス H が https://hesokuri.example.com/
で展開されているとしましょう。これとは別に統合資産管理サービス S が https://shisan.example.com/
で展開されています。サービス H はヘソクリ情報にアクセスできる API を JSONP で提供しており、サービス S もこの API を利用し、ヘソクリ以外の資産も含めて統合的に管理できる機能をユーザに提供しています。
A さんは、サービス H とサービス S を両方利用しています。
- A さんがサービス H にログインし、資産管理ページ、
https://shisan.example.com/kanri
にアクセスしたとします。
- ここでサイト H は
https://hesokuri.example.com/checkUserStatus?site=shisan
にリダイレクトするよう A さんのブラウザに返します。
- するとブラウザからサイト H にリクエストが飛びます。
- サイト H では、
checkUserStatus
が呼ばれたら、クエリストリングの site
が事前に登録されているものかチェックします。shisan
は正規の提携先として登録されているのでよしとします。
- 次にリクエストしてきたユーザがログイン済みかをチェックします。ログイン済みでないならログイン画面を出し、ログインさせます。あと、サービス S がサービス H 内の A さんのヘソクリ情報にアクセスするのを許可するかどうかを聞く画面を出します。ここでは、A さんがログイン状態になり、サービス S にヘソクリ情報を使ってもいいと許可したことにします。
- サイト H は、A さんが「許可」ボタンを押した後のページとして、HTML を返さず、代わりに
https://shisan.example.com/accepted/
にリダイレクトするよう指示を出します。この元のサービスに戻る URL は、最初の site=shisan
に紐付くデータとして、サイト H のデータベースに格納されています。
- ブラウザは
https://shisan.example.comn/accepted
にリダイレクトします。
- サイト S では、資産管理画面を出します。この画面の html 中に、
<script src="https://hesokuri.example.com/getHesokuriInfo.js?callback=listHesokuriInfo" />
というタグが含まれています。
- A さんのブラウザがこの html をレンダリングしようとすると、
https://hesokuri.example.com/getHesokuriInfo.js?callback=listHesokuriInfo
へのアクセスが発生します。このリクエストには、ログイン認証済みの証しが cookie に載って飛びます。(JSONP 呼び出し)
- サイト H は、cookie の情報に基づき、A さんのヘソクリ情報をデータベースから取得し、
listHesokuriInfo([{ place: "冷凍庫の製氷機の下", amount: 150000}]);
を返します。A さんは製氷機の下に 15 万円隠しているようです。もちろん、この関数名は、JSONP 呼び出しのときの callback パラメータで渡ってきた値を利用しています。
- ブラウザでは返ってきた JavaScript を評価するわけですが、
listHesokuriInfo([{ place: '冷凍庫の製氷機の下', amount: 150000}]);
なので、この関数の実行となります。これは 8. で返ってきた html の中で、JSONP 呼び出しより前に定義されている関数です。サイト S の資産管理画面の該当部分にやってきたデータをはめこんだりします。
まあ、これが JSONP で API を提供しているサイト H と、それを利用したサービスを展開するサイト S の連携です。
で、これを悪用するのがサイト N https://nusutto.example.com/
です。
- A さんがログイン状態で
https://nusutto.example.com/
を踏んでしまったとしましょう。
- サイト N では画面を html で返しますが、この html 中に、
<script src="https://hesokuri.example.com/getHesokuriInfo.js?callback=sendHesokuriInfo" />
というタグが含まれています。
- A さんのブラウザがこの html をレンダリングしようとすると、
https://hesokuri.example.com/getHesokuriInfo.js?callback=send
へのアクセスが発生します。このリクエストには、ログイン認証済みの証しが cookie に載って飛びます。(JSONP 呼び出し)
- サイト H は、cookie の情報に基づき、A さんのヘソクリ情報をデータベースから取得し、
sendHesokuriInfo([{ place: "冷凍庫の製氷機の下", amount: 150000}]);
を返します。(CSRF 成功)
- ブラウザでは返ってきた JavaScript を評価するわけですが、
sendHesokuriInfo([{ place: '冷凍庫の製氷機の下', amount: 150000}]);
なので、この関数の実行となります。サイト N が用意しているこの関数は、盗んだヘソクリ情報をあれこれ悪用します。
CSRF 攻撃は、ブラウザからのアクセスからは正規のものと非正規のものとが区別できない、ということから可能になってしまいます。CSRF 対策は、けっきょく、正規の一連の流れの中におけるリクエストなのか、を判断するしかありません。一般には、CSRF 対策が必要なページに先立つページでトークンを発行し、WEB アプリ側のセッションに格納しておきます。CSRF 対策が必要なページでトークンがリクエストに乗る仕組みにしておき、リクエストが来たときにそこに乗っているトークンとセッションに格納したトークンが一致すれば正規のリクエストとすることで、CSRF を防ぐことができます。
JSONP はそもそもそういう流れがないクロスドメイン API ですが、先立ってトークンを用意しておいて、あとでリクエスト時に渡されるトークンとの一致で見るしかないかと思います。やりかたはいろいろあると思いますが、たとえば、先の正規の流れの 6. のところでサイト H が返すリダイレクト先を https://shisan.example.com/accepted/?token=x2032
とすることかと思います。トークンの実際の値は 5. の段階で発行し、セッションにも格納しておきます。で、サイト S が 8. で JSONP を呼び出す際、このトークンを利用し、<script src="https://hesokuri.example.com/getHesokuriInfo.js?callback=listHesokuriInfo&token=x2032" />
というタグを生成する、と。
かなり適当に書いたので穴はあるかもしれませんが、どうでしょうか。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2017/09/06 12:01
2017/09/06 12:11
退会済みユーザー
2017/09/06 12:25
2017/09/06 14:18