今回は、ウェブセキュリティを考えるうえで重要な脆弱性、クロスサイトリクエストフォージェリについて学んでいきます!

クロスサイトリクエストフォージェリーって何?

今回は、Cross-Site Request Forgeriesについて学んでいきます。略して『シーサーフ』と呼ばれることも多い脆弱性で、知らないうちに操作される攻撃です!

知らないうちに操作ってどういうこと?

簡単に言うと、ユーザーが意図しないのに、勝手に操作がされてしまうってことだよ。例えば、パスワードが勝手に変更されたり、銀行から勝手に振込されてしまったりがあるよ。

えっ、怖いね。でも、どうやってそんなことができるの?

まず、CSRFが起こる仕組みを説明するね。例えば、あなたが銀行のWebサイトにログインしているとします。ログインしてるとCookieにセッション情報が保存されています。

で、ここで別のタブで悪意のあるWebサイトを開いてしまったとするよ。

そうすると、このサイトには銀行への送金処理を行うリクエストが仕込まれていて、勝手にリクエストが送られてしまいました!

え!別のサイトから銀行のサイトにリクエストを送れるの?

銀行のWebサイト側できちんと対策がされていないと、実は送れちゃうんです。

しかも、ログイン情報が詰まったCookieの情報も一緒に送られるから、銀行のシステムは『正規のリクエストだ』と思い込み、振込を完了してしまいます。

こうしてお金が攻撃者の口座へ…。

こわ!自分が知らないうちに、悪意のあるサイトが銀行のサイトにリクエストを送ってるってことなんだね。。。

CSRFの具体的な攻撃例

では、具体的な攻撃例を見ていきましょう。

例えば、銀行サイトで送金するときのリクエストがこんな感じだとします

これは送金処理のリクエストなんだね。

そうそう。

で、攻撃者は自分のWebサイトに以下のようなHTMLを仕込んでおきます

さっきのリクエストと同じ内容のリクエストを送るフォームだね。このフォームが自動で送信されちゃうってこと?

その通り!

scriptタグの中で、hackというidの要素をsubmit()するという処理が書かれているね。被害者がこのページを開くと、自動的にformが送信されて送金のリクエストが送られてしまいます。しかも、被害者がログイン中なら、ログイン情報が詰まったCookieも一緒に送られるから、正規のリクエストとして処理されちゃうんです。

CSRFの対策方法

では、CSRFへの対策方法を説明していきましょう。

CSRFにはいくつかの対策方法があるよ。1つ目は「CSRF対策トークン」です。

セッションごとまたはリクエストごとに、ランダムな文字列であるトークンを生成して、フォームに埋め込んでおくよ。そして、リクエストが来たときに、このトークンが正しいかサーバー側でチェックします。CSRF対策トークンは推測できないように、暗号論的に安全な乱数を使う必要があります。

なるほど。悪意のあるサイトはこのトークンを知る術がないから、正しいリクエストが作れないってことだね!

その通り!

でも、本当にCSRF対策トークンが漏れることってないの?

実は、CSRF対策トークンも完全に安全というわけではなく、流出してしまう危険性もあります。CSRF対策トークン自体が弱いわけではないんだけど、他のセキュリティリスクによって漏れる可能性があるよ。

どんな場合に危険なの?

まず、XSS(クロスサイトスクリプティング)脆弱性がサイトにある場合です。JavaScriptで画面上のトークンを読み取られる可能性があります。

あ!前回の動画でやったXSSと、CSRFって関係があるんだ!

そうそう。XSS脆弱性があると、CSRF攻撃が成立する可能性があるよ。だからXSS対策も同時にしっかりする必要があります。

他にはどんな危険性があるの?

HTTPS通信を使っていない場合、すなわちHTTP通信の場合、通信を盗聴されてトークンが漏れる可能性があります。

また、トークンをURLに含めていると、リファラーヘッダーを通じて他サイトに漏れたり、ブラウザやアプリのキャッシュに保存され、不正アクセスされる危険があるよ。

色々危険があるんだね。うーん、CSRF対策トークンって意味ないんじゃない?

そんなことはないよ!

CSRF対策トークンは、多くの攻撃を防ぐ基本的なセキュリティ対策のひとつで、他の対策と組み合わせることで非常に強力になります。

なるほど。トークンを使うだけじゃなくて、トークンの管理方法も大事なんだね!

その通り!

2つ目の対策は「SameSite属性の設定」です。これは、Cookieの属性の1つで、異なるサイトからのリクエストではCookieを送信しないようにする設定です。

それならログイン情報が入ったCookieが送られないから、さっきの例であった攻撃は防げるね!

うんうん!簡単に設定可能で、一定の攻撃を防ぐことができるので、特に理由がない限り、SameSite=Strictという設定にしておくのがおすすめです。

3つ目は「Refererチェック」です。これは、リクエストがどのページから来たのかを確認する方法です。

「Referer」はリクエストヘッダーの中にあるフィールドで、そこに発信元URLが記載されているんだったね。でも、Refererって改ざんできないの?

いい質問だね。すふぃあの言う通り、Refererヘッダーは偽装することが可能です。たとえば、不正なクライアントがリクエストを送信する際にRefererを手動で改ざんしたり、ブラウザの設定や拡張機能によって送信されないように設定されるケースもあります。そのため、この対策だけに頼るのは危険です!

じゃあ、意味がないの?

意味がないわけではないよ。単独では信頼性が低いけれど、他の対策と組み合わせることで、より強力なセキュリティを実現できるよ。

他にも、実装する上で気をつけるべきポイントがいくつかあるよ。

どんなことに気をつければいいの?

まず、GET要求に対してはデータを更新するような処理を実装しないことです。GETは情報の取得だけに使うようにしましょう。

なるほど。データを更新する処理は全部POSTにするんだね。でも、GETとPOSTの違いって、パラメータの渡し方が違うだけじゃないの?

一番大きな違いは、GETはURLにパラメータが全部表示されることなんです。例えばこんな感じ

あぁ、URLの後ろに「?」がついて、その後にパラメータが来るやつだね!

そうです。このURLを見ると「delete」という処理で「id=123」というパラメータを渡していることが分かるね。これが危険な理由を説明していきます。

まず、ブラウザは画像を表示する時、その画像のURLに対してGET要求を自動的に送信します。つまり、こんなHTMLを書くとさっきのリクエストが送信できちゃいます。

え!もしかして、画像を表示しようとしただけなのに、deleteの処理が実行されちゃうの?

その通り!しかも、ユーザーがページを開いた瞬間に自動的に実行されてしまいます。

なるほど。POSTだとこういう攻撃はできないの?

POSTの場合は、単純な<img>タグではリクエストを送れません。フォームを作って送信する必要があります。そのため、この種の攻撃はより難しくなります。

さらに、GETリクエストのURLは他にも問題があります。ブラウザの履歴に残ったり、ブックマークされたり、他のサイトにリファラーとして送信されたりする可能性があるんです。

あ!データを更新するURLが履歴に残ってて、後でそれをクリックしたら...

その通り!意図せずに更新処理が実行されてしまう可能性があります。だから、GETはデータの参照だけに使って、更新はPOSTを使うというルールが重要なんだよ。

さらに、重要な操作をするときは、パスワード再入力を求めるなどの追加の認証を入れるのもいい方法です。

二重の確認になるから、より安全になるってことだね!

それから、APIを作るときは、できるだけJSONを使うようにしましょう。JSONの場合、Content-Typeヘッダーが異なるため、単純なフォーム送信ではCSRF攻撃ができなくなります。

へぇ、APIの形式を工夫するだけでも、セキュリティが上がるんだ!

そうそう。セキュリティって、色々な層で対策を重ねていくことが大切なんだよ。

まとめ

今回は、CSRFについて説明しました。

CSRFは、ユーザーが意図しない操作を勝手に行わせる攻撃で、対策には、CSRF対策トークン、SameSite属性、Referrerチェックなどがあり、複数の対策を組み合わせることが重要だよ。

動画の方も、是非是非ご覧ください!