MENU

CSRFについて

2021 3/16

どうもすなです。

セキュリティについて学ぼうシリーズが前回からだいぶ間が空いてしまった。。。

今回はクロスサイト・リクエストフォージェリ(CSRF)について。
これ、シーサーフって読むんですよ(ドヤ顔

僕もつい先日他の人の発表で知りました(0.5秒で白状

さて、さっそく概要について見ていく

目次

CSRFとは

CSRF脆弱性は重要な処理が行われる際に正規の利用者かどうかを判別していないために起こる脆弱性である。

具体的にはCSRF脆弱性があると、以下のような影響が出てしまう可能性がある。

・利用者のアカウントによる物品の購入
・利用者のパスワードやメールアドレス などのアカウント情報改竄
・利用者のアカウントによるSNSへの書き込み

ようするに意図したリクエストじゃないリクエストが勝手に送られてしまうというもの。

どういうふうに攻撃されるのか詳細を見る前に、まず全体像を把握する。
結論、(反射型の)XSSの攻撃手法とかなり似ている。

XSSについてはこちらを参照。
https://www.sunapro.com/xss-overview/

①〜③までは同じ。
XSSでは④でレスポンスを受け取った後で、クライアントのブラウザでJavaScriptが実行される一方で、
CSRFでは③のリクエストに対するサーバー側の処理を悪用されるということ。

そのため、CSRFでの悪用内容は、もともとサーバー側で用意された処理に限定されるが、XSSではブラウザ上でできることはなんでもできてしまう。
こういった理由から、攻撃範囲はXSSの方が広く認知度も高いのが事実。

ただしCSRFもちゃんと対策はすべき。
CSRFはサーバーにリクエストが到達するだけで攻撃が成立するため、XSS対策はできていてもCSRF対策ができていないなんてこともあり得る。
ここは後で詳しく見ていく。

実際の攻撃手法

XSSと同じような感じで罠サイトを作ってそこにユーザーを誘導させてアクションを起こさせる。
言葉で説明より見た方が早そうなのでまずは図を参照。

例えばパスワード変更ページであるとすると、実際にはパスワードを入力してPOSTする。
罠サイトでは別の適当なページとボタンを用意してそのボタンを押すと、攻撃者の任意のパスワードに書き換えられる

実際にはこんなような感じ。

星座占いとかいうポップな見た目なくせに実は”cracked”という文字列でパスワードを送信しようとしている。

これがPHPとかの同期通信によるリクエストの場合は正規ルートと罠ルートで異なるのはReferrerとOriginのみだが、サーバー側ではこれらのヘッダを確認しないため、これでパスワードが書き換わってしまう。

ちなみにCSRFでは攻撃者は画面を参照できずサーバーで攻撃が完結するので、XSSと違いレスポンス後に攻撃者になんらかの情報を送ることもできないため、情報を盗み出すことはできない。

しかし、CSRFによってパスワードの変更がされた場合は、変更後のパスワードは攻撃者の意図したものになっているため、不正ログインにつながる可能性がある。

JavaScriptによる非同期通信の場合は?

上記ではformタグによる同期通信の場合を見てきた。
ではJavaScriptによる非同期通信の場合はどうなるのか?

基本的には同期通信の時と同じようにリクエストがいく。
リクエスト時に違うのは同期通信の時と同様ReferrerとOriginであり、Originは罠サイトのhttp://trap.comなどになっている。

この時罠サイトのOriginがhttp://trap.comで、リクエストしているAPIがhttp://example.comだった場合、クロスドメインになるのでAccess-Control-Allow-Originでクロスドメインを許可していない限り、レスポンスエラーになる。

じゃあこの場合は何もせずともCSRF対策できているのかというとそうではない。

この時検証ツールを見てみると、クロスオリジン要求をブロックしたという内容のエラーが出ているが、これはあくまでもサーバー側からのレスポンスを読み取れなかったということ。

しかしCSRFはサーバーにリクエストが到達した時点で成立するので、レスポンスをブラウザが読み取れるかどうかは関係ない。

したがって同期通信 / 非同期通信に関わらずCSRFの対策はすべき。

対策方法

まずそもそもCSRF脆弱性が生まれる原因をおさらいすると、ログインユーザーが本来正規ルートで行うべきリクエストを、外部の攻撃者がそのログインユーザーの情報に乗っかってリクエストできてしまうことが原因である。

つまり根本的な対策としては、そのリクエストが本当に正規ルートで意図したものであることを確認できれば良い。

そのための方法を4つ挙げる。
(本では3つだったが、もう少し調べるとSPAなどの場合に良さげなのがあったので4つ目として追加)

1. Referrerのチェック
2. パスワードの再入力
3. トークンの利用
4. プリフライトリクエストによる検証

1. Referrer

正規のリクエストとCSRF攻撃によるリクエストではReferrerのドメインが異なっている。
したがってReferrerをチェックして意図していないものだった場合はリクエストを弾くというようにすれば対策になる。

ただし、ブラウザの設定でReferrerを送信しないように設定している場合があるため、その場合は正規リクエストなのに受け付けないということになってしまう。
車内システムなどの利用者が限定できる環境なら手軽にできてよき。

2. パスワードの再入力

重要な処理の前に入れると本人確認もできてよき。
GitHubとかそうなってる気がする。

でも結局本人確認させても実際にリクエスト送るのはその後のページなんだから、その後のページの内容を攻撃者が偽装すれば対策にならないのでは?と思ってしまった。。。

3. トークンの利用

これが多分一般的に用いられている手法。

ようするに攻撃者が好き勝手罠サイト作ってそこからリクエストした時に通るのが問題なので、
正規ルートの場合にしか発行されないトークンを用意してそれも合わせて認証しましょうという話。

つまり行いたい重要な処理の前にトークンを発行する必要がある。

PHPとかの場合はその場でトークンを発行して、実際の処理を行うページに遷移するタイミングでhiddenタグなどで渡すかセッションに格納する。
リクエスト前にそのトークンが存在していて照合できたらリクエストを送るようにすれば良い。

SPAなどの場合は一度にトークン生成用の外部APIを叩いてトークンをもらう。
そのトークンを元にリクエストを行い、サーバー側でトークンの検証を行う。

こうすれば外部サイトはトークンをもらう術がなくなるため、よい対策になる。

この方法は古くから知られていて既存のフレームワークの仕組みに取り込まれていたりしているため、実績はあり分かり易い仕組みではあるものの、フロントをSPAにしているとわざわざトークン生成用のAPI作って叩いてーってやらなきゃいけなくなる。
あとトークン情報をサーバー側で保管しておかなきゃいけなくなるのでなんかめんどいなーと思って調べたらいいのがあった。それが次のプリフライト。

4. プリフライトリクエストによる検証

プリフライトリクエストとは、シンプルなリクエストじゃないリクエストの場合にPOSTなどの実際のリクエスト前にこのリクエストを送っても大丈夫かを確認するためのOPTIONメソッドを使ったリクエストのこと。

シンプルなリクエストとは?プリフライトリクエストとは?って感じの時は以前まとめた記事参照。

あわせて読みたい
クロスオリジンとCORS
クロスオリジンとCORSどうも、すなです。ありがたいことに業務ではフロントエンドもバックエンドもAWSも触らせてもらってて今後自分がどの方向にいくのか不明ですが、いずれにしてもセキュリ...

プリフライトリクエストを発生させる条件はいくつかあるが、カスタムヘッダを付与する方法が一般的。
そしてサーバー側ではカスタムヘッダが存在することを確認すればよい。

仮にカスタムヘッダを攻撃者が偽装してもOriginが許可されていなければプリフライトリクエストのレスポンスは失敗になるので、攻撃リクエストのPOSTが行われないことになる。

この方法のメリットはトークンなどのようにサーバー側で特に何かを管理する必要がなく、OPTIONメソッドを受け付けてカスタムヘッダを検証する処理を追加するだけで良くなること。
クライアント側もカスタムヘッダを付与してリクエストするだけでよくなる。

ちなみにformなどの同期通信の場合はカスタムヘッダを付与することができないため、この手法の場合そもそもformによるリクエストはできない。

これはデメリットでもあるが、裏を返せばSPAなどの構成ではJavaScript以外からの想定外のアクセスを問答無用で一律排除できるので、同期通信をしなくてもいいならむしろメリットになりうる。

CookieのSameSite = Laxについて

Chromeでは現在CookieのSameSite=Laxがデフォルトになっている。

これによってCookieのdomainオプションと違うドメインからのPOSTやAjaxリクエストについてはサーバーにCookieを送信されなくなる。

したがってAPIのリクエストがCookieのdomainと同じであれば認証CookieにSameSite=Laxを指定することでCSRF対策になりうる。

APIリクエストがクロスドメインの場合は使えない。

かなり手軽にできてよさそうだけど、根本対策ではないらしい?から別で上記のような根本的な対策をした方が安全かも。

まとめ

攻撃者が直接攻撃するんじゃなくて赤の他人の認証情報を利用するってのがなんともセコい。
全然知らんかったけど、昔mixiではまちちゃん事件なるものが勃発したらしい。

まとめててSPAの時はどうなんだろうと思って調べたらさらに良さげな記事見つけて知識が深まったので本当アウトプットしたかいがあった。

セキュリティ関連はあとやったとしてもセッションハイジャックだけかな
それ終わったら一旦終了にする

まとめた後何回か見直す機会あったから本当このシリーズまとめて良かったと思う。
偉いぞ過去の自分。

参考

あわせて読みたい
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践
Qiita
Web API の CSRF 対策まとめ【追記あり】 - Qiita
Web API の CSRF 対策まとめ【追記あり】 - Qiitaセキュリティ脆弱性診断などでたまに CSRF について指摘されることがあります。 今まではトークン発行して対応すれば良いんでしょ? と思ってましたが、SPA のように非同期...
30歳からのプログラミング
SPA の CSRF 対策や CORS について検証する - 30歳からのプログラミング
SPA の CSRF 対策や CORS について検証する - 30歳からのプログラミングSPAのCSRF対策について調べる機会があったので、まとめておく。 CORSの挙動を中心に、実際にコードを書いて検証していった。 間違ったことは書いていないと思うけど、利用...

この記事が気に入ったら
フォローしてね!

この記事を書いた人

コメント

コメントする

目次
閉じる