ふと気になって調べたことの備忘メモです ✍
(2022/11/3追記)ご指摘頂いた内容を踏まえて加筆修正をおこないました
Webアプリケーションの開発に携わっているとCSRF という脆弱性への対処を求められますが、多くの場合利用しているフレームワークが設定追加だけで対応してくれたり、既に前任者によって適切な処置がされていたりなど、実務上で目を向ける機会はその重要性と比較して少ないのでないかと思います
また、Webブラウザの実装やHTTP周辺の関連仕様の変化から陳腐化している情報も多く、現代において全体感と具体的な対処法を理解するには少しばかりハードルが高いように感じていました
ですので、自身の現時点での認識を明文化して残しておくことにしました
なお、私はWebセキュリティの専門家でなく、一介の開発者のため、誤りが多分に含まれる可能性があります
ご指摘を頂ければ修正したいと思います🙏
一般的な名称はCross-Site Request Forgery(クロスサイトリクエストフォージェリ) とされ、他にもCSRF(シーサーフ / シーエスアールエフ) 、リクエスト強要 、Session Riding(セッションライディング) 、XSRF などの呼び方がある
https://ja.wikipedia.org/wiki/クロスサイトリクエストフォージェリ
直訳するとサイト横断リクエスト偽造 攻撃といったところだろうか
ここで言うサイト横断 とは、攻撃対象のWebサイト以外の場所から(発生したHTTPリクエスト) というニュアンスを持っており、例えば以下のような状況を示す
上記のようなコンテキストにおいて、何らかの方法によって攻撃対象のWebサイト宛のHTTPリクエストが発生し、意図しない更新操作 を発生させてしまう脆弱性をCSRFと呼ぶ
何らかの方法については、例えば以下が考えられる
高木浩光氏による解説 によると、国内・国外共に初出は2001年頃であるらしい
https://www.ipa.go.jp/security/vuln/event/documents/20060228_3.pdf
近年の動向としては注目度自体は他の脆弱性と比較して高くなく、例えばOWASP(Open Web Application Security Project) が公開している脆弱性のトレンドを示すOWASP Top10 の2021年版においては選外となっている

https://owasp.org/Top10/ja/ より引用
ちなみに、2013 年版では 8 位にあった ようなので、傾向として発生数が減少しているものと思われる
https://owasp.org/www-pdf-archive//OWASP_LA_New_OWASP_Top_10_David_Caissy_2017_07.pdf

https://owasp.org/www-pdf-archive//OWASP_LA_New_OWASP_Top_10_David_Caissy_2017_07.pdf より引用(p6)
また、IPAが国内で発生した脆弱性についてまとめているソフトウェア等の脆弱性関連情報に関する届出状況 においても、報告の累計件数や直近の発生件数においても数自体は多くない
https://www.ipa.go.jp/security/vuln/report/vuln2021q4.html

https://www.ipa.go.jp/files/000095630.pdf より引用(p18)
しかしながら、2021年においてもEC-CUBE の管理機能において CSRF 脆弱性が発見される など、定期的に脆弱性の報告がなされている状況である
https://www.ec-cube.net/info/weakness/20211111/
IPA(情報処理推進機構) が公開している安全なウェブサイトの作り方 にて紹介されている図がわかりやすい
https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_6.html

https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_6.html より引用
攻撃の理解にあたっては、特に以下について注目するとよい
日本における著名なCSRF攻撃としてははまちちゃん騒動 が紹介されることが多い
https://ascii.jp/elem/000/000/063/63560/
具体的な攻撃方法については、以下のブログに詳しく説明されていた
攻撃者(はまちや氏)自身で攻撃対象のWebサイト(mixi)に開設していたアカウントの日記機能に悪意のあるWebサイト(はまちや氏の個人サイトの攻撃用ページ)へのリンクが投稿され、
ログイン済の利用者がリンクをクリックすると自身の日記に意図しない投稿がおこなわれてしまう…という脆弱性だったとされる
https://mohritaroh.hateblo.jp/entry/20050420/1113967566
対象のWebサイトが設計上意図しない更新操作を受け付けてしまう ことで攻撃が成立してしまうことは既に述べたが、具体的にどのような実装が問題となりうるかについて、一般的なパターンを確認していく
近年のWebアプリケーションはSPA(Single Page Application) と任意のWeb API(RESTなど)を組み合わせてデータの取得や更新をおこなうことが多いが、以前は<form> タグにより送信されたフォームデータをWebサーバーで処理し、テンプレートエンジンを用いてHTMLに値をレンダリングしたものを新たなページとしてブラウザに返却する形式が一般的であった
https://developer.mozilla.org/ja/docs/Learn/Forms/Sending_and_retrieving_form_data
例えば、Webサーバから以下のHTMLが返却されたとする
<formaction="https://example.com/greeting"method="POST"> Say:<inputname="say"value="Hi"><br/> To:<inputname="to"value="Mom"><br/><button>Send my greetings</button></form>これに対して、ブラウザに表示されたフォームのSend my greetings ボタンをクリックすると、以下のようなHTTPリクエストがhttps://example.com/greeting に向けて送信される

フォームの表示イメージ
POST /greeting HTTP/2.0Host: example.comContent-Type: application/x-www-form-urlencodedContent-Length: 13say=Hi&to=Momこの動作自体は一般的なものであり問題ないが、HTML フォーム送信時の仕様 において、index.html がexample.com 以外のいかなるドメインから配信されていたとしてもaction で指定されたURLにリクエストが発生する仕様となっており、これがCSRF攻撃を発生させる原因のひとつとなっている
https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm
この挙動は現代におけるユースケースから考えると望ましい挙動ではないが、Webに関する仕様の多くが後方互換性を強く意識して改訂されてきている経緯から維持されていると思われる
https://standards.mitsue.co.jp/archives/001233.html
悪用すると、例えば以下のようなHTMLをevil.com で公開した上で、何らかの方法でユーザーにevil.com へアクセスさせることができれば、ユーザーが望まない任意のPOSTリクエストをexample.com 宛に送信できる
<formaction="https://example.com/greeting"method="POST"><inputname="say"value="Go away!!!!!"><inputname="to"value="Dad"></form><script>// ただちにフォーム送信を実施document.querySelector('form').submit();</script>もしもhttps://example.com/greeting がHTTP Cookie を用いてログイン中のユーザーとして挨拶を投稿するエンドポイントであった場合は、第三者からログイン中のユーザに関連するデータ更新が可能となるため、サービス運営側の観点としてはより危険度が高いと言えるが、
CSRF攻撃の成立条件はあくまでHTTP リクエストが正規の Web サイトから発せられたものか検証せずに受け付けてしまう ことにあるため、ユーザーのログイン有無自体は直接的な原因とはならない
一方で、Webサイトからのセッション Cookie の発行有無は CSRF 抑止にあたり重要な意味を持つ ことにも留意する。これについては後述する
https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies
ログイン機能が無いWebサイトで深刻な被害が発生した一例として、2012年のパソコン遠隔操作事件 では、問い合わせフォームに対してCSRFへの脆弱性を悪用して犯罪予告文を書き込ませる…というものがあった
https://ja.wikipedia.org/wiki/パソコン遠隔操作事件
前節で説明した<form> タグを用いたフォームデータ送信以外の方法でも、脆弱性を持ったエンドポイントに対してHTTPリクエストを発生させることができれば攻撃は成立させられる
最も一般的な方法として、https://example.com/greeting?say=Hi&to=Mom に示すようなクエリストリングを付与したURLをWebブラウザやメーラー等でクリックすると、example.com に対して以下のHTTPリクエストが送信される
https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/Identifying_resources_on_the_Web#query
GET /greeting?say=Hi&to=Mom HTTP/2.0Host: example.comこのケースにおいて、/greeting エンドポイントがPOSTだけでなくGETによるデータ更新も受け付ける実装になっている場合、evil.com などで攻撃用HTMLを配信せずともURLを用意してユーザに踏ませるだけで攻撃を成立させることができる
対策については後述するが、POSTエンドポイントと同様にURL ないしリクエストが正規の Web サイトから発行されたものか検証する ことが望ましい
節を割いて紹介しなかった一般的なユースケースのひとつとして、SPAからREST APIのエンドポイントに対して更新操作をおこなう場合が考えられる
今まで紹介してきたフォーム送信やURLへのリソース要求は、いずれもWebブラウザにおいてページ書き換えが発生するものだったが、XMLHttpRequest やFetch API などによりおこなわれるHTTPリクエストは、フォーム送信と比較して以下のような特徴がある
PUT やDELETE 、PATCH といった任意のHTTPメソッドを発行できるSame-Origin Policy を指す。後述これらのエンドポイントについても、今まで説明してきたのと同様にHTTP リクエストが正規の Web サイトから発行されたものか検証 しないと攻撃が成立する可能性が高い
しかしながら、幾つかのブラウザの挙動やHTTP関連の仕様の影響で攻撃が抑止される場合があり、それらも加味した実装を対策として紹介している記事も多いが、今回はCSRF攻撃への根本的な対策方法について検討していく
攻撃の抑止方法の詳細に踏み込む前に、実行環境にあたるWebブラウザの挙動に関連する幾つかの仕様について確認しておく
脆弱性に複数の対策を織り込むことは多層防御 の観点からも望ましい
https://e-words.jp/w/多層防御.html
成立条件 の節ではHTMLフォームからのPOST通信について説明したが、<form> タグは特定の属性を付与することで以下の形式のHTTPリクエストを発行することができる
https://developer.mozilla.org/ja/docs/Web/HTML/Element/form
methodにて指定)application/x-www-form-urlencodedmultipart/form-datatext/plain注意すべき点として、例えばtext/plain のMIMEタイプを利用して以下のようなフォームを作成すると、JSONとしてパース可能なペイロードを送信することができる
本来であれば、Content-Type: application/json の場合に限ってリクエストの内容をJSONとして解釈するのが望ましいが、利用しているWebフレームワークの仕様や設定について確認することを推奨する
<formaction="https://example.com/api/greeting"method="POST"enctype="text/plain"><inputname='{"say": "Go away!!!!!", "to": "Dad", "trash": "'value='"}'/><button>Attack</button></form>{"say":"Go away!!!!!","to":"Dad","trash":"="}なお、次節で説明する同一オリジンポリシーの文脈において、HTMLフォームから送信できる範囲のHTTPリクエスト(に類似したもの)を単純リクエスト(Simple request) として定義している
これは、HTMLフォームから送信したHTTPリクエストはプリフライトリクエストを発生させない ということを意味する
https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#単純リクエスト
この挙動は、同一オリジンポリシーの誕生より前にHTMLフォームが存在していたことから、後方互換性を壊さないために規定されているものらしい
https://zenn.dev/qnighy/articles/6ff23c47018380#preflightが不要なケース
Webの世界においては、Webブラウザから複数ドメインに対して通信が発生するケースにおいて、いかにコンテンツの安全性を担保するか…といったことが古くから課題となっており、その文脈において同一オリジンポリシー(Same-Origin Policy / SOP / 同一生成元ポリシー) という考え方が浸透している
ページのプロトコル、ポート番号、ホスト名が同一 の場合を同一オリジン、値が異なる場合はクロスオリジンと定義し、リクエストの送信やレスポンスの取得に一定の制限を課すものである
https://developer.mozilla.org/ja/docs/Web/Security/Same-origin_policy
この制限をCORS(Cross-Origin Resource Sharing / オリジン間リソース共有) と呼ぶ
https://developer.mozilla.org/ja/docs/Web/HTTP/CORS
CORSについて、特にCSRFの文脈においては、プリフライトリクエスト(Preflight request)について理解しておくとよい
https://developer.mozilla.org/ja/docs/Glossary/Preflight_request
プリフライトリクエストは、特にWebアプリケーションをSPAとバックエンドのREST APIにて構成している場合において、API側でクロスオリジンからのリクエスト可否を決定するための機構である
HTTPリクエストがクロスオリジン、かつ前述の単純リクエストに当てはまらない 時に、ブラウザ側でエンドポイントに対してOPTIONSメソッドを発行し、バックエンドにて生成されたレスポンスのHTTPヘッダの内容に応じてメインリクエストを送信するかどうか決定する

https://developer.mozilla.org/ja/docs/Web/HTTP/CORS より引用
一連の動作はWebブラウザにてアプリケーションコードから見て透過的に実施されるため、結果として以下に挙げたような単純リクエストに当てはまらない HTTP リクエストでは、通常 CSRF 攻撃は成立しない と言える
Content-Type: application/json ヘッダが指定された場合のみ処理を受け付けるエンドポイントへのリクエストX-Requested-With: XMLHttpRequest ヘッダなど、特定の固定ヘッダが設定されている場合のみ処理を受け付けるエンドポイントへのリクエストもっとも、この挙動はクロスドメイン通信を許可する設定がバックエンド側にておこなわれていない(Access-Control-Allow-Origin: * などのヘッダを返却しない)ことが前提となるため、根本的な対策としては後述する手法を選択するのが望ましいと思われる
もう一点注意すべきこととしては、単純リクエストとしてプリフライトリクエストが発生せず送信されたHTTPリクエストの場合、CORS の制限によりレスポンスの読み取りが失敗したとしても CSRF 攻撃は成立しうる 可能性が高い

プリフライトリクエストの段階で失敗した例(図上段)と、プリフライトリクエスト通過後レスポンス読み取りで失敗した例(図下段)
プリフライトリクエストが発生し、かつ失敗した場合のメッセージは以下(太字は筆者が注記)
この場合はOPTIONSリクエストまでしか発生しておらず、CSRF を成立させるための本体のリクエストは発生していない 状況といえる
Access to fetch at '.....' from origin '.....' has been blocked by CORS policy:Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
プリフライトリクエストを通過し、ブラウザ上でのレスポンスの読み取りの段階でエラーとなった場合のメッセージは以下となる
この場合は実際にリクエストが発生してしまっているため、ブラウザでレスポンスが読み取れるかどうかに関わらず攻撃は成立している 可能性が高い
Access to fetch at '.....' from origin '.....' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
なお、プリフライトリクエストの仕様はWebブラウザに固有のもので、例えばCLI上でのcurl コマンドや、PythonにおけるRequests モジュールの利用など、Webブラウザ以外の環境から発生するリクエストについては基本的に制約は課されない
近年、CSRFの緩和を目的として導入された仕様としてHTTP Cookie の SameSite 属性がある
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Set-Cookie/SameSite
これは、WebサイトよりSet-Cookie ヘッダでCookie付与をおこなう際に、以下の属性を追加で指定することでクロスサイト(歴史的経緯よりクロスオリジンではないことに注意)での通信においてアクセス先にCookieが付与されなくなる
SameSite=Lax の場合GET / HEAD / OPTIONS / TRACE のいずれかである場合のみCookieが送信されるSameSite=Strict の場合SameSite=None の場合一般的なWebサイトにおいて、従来の動作を維持しながらCSRF攻撃への対策をするには、セッション Cookie にSameSite=Lax を指定する のが望ましい
この挙動は、外部サイトからAタグ等によりURL遷移でサイトを訪れた際にはリクエストにCookieが付与される一方で、CSRF攻撃のリスクとなり得る別サイトからのPOSTリクエストにはセッションCookieが付与されず、ログインが必要なエンドポイントに対するアクセス全般が不成立となる
ただし、前項で説明したとおりCSRF攻撃はユーザーのログイン有無自体は直接的な原因とはならず、HTTP リクエストが正規の Web サイトから発行されたものか検証していない場合に成立する(先に説明したパソコン遠隔操作事件の事例を参照)ため、後述する根本対応の実施をおこなうことが望ましい
https://ja.wikipedia.org/wiki/パソコン遠隔操作事件
ちなみに、2022年現在の状況として、一部ブラウザでSameSite属性が未指定の場合のデフォルトの挙動がLaxに変更されているらしく、対策をおこなっていない既存サイトの安全性が向上しつつある
その他にも2分間ルール といった複雑な挙動があり、詳細は以下に詳しい
https://blog.tokumaru.org/2022/01/impact-conditions-for-no-CSRF-protection-sites.html
SameSite Cookieに関する解説は以下に詳しい
https://blog.jxck.io/entries/2018-10-26/same-site-cookie.html
CSRFを抑止するために必要となるアプリケーション設計については、OWASP Cheat Sheet Seriesで詳しく紹介されている
本記事では、中でもToken Based Mitigation として紹介されている2種類の方法について解説する
今まで説明してきた通り、CSRF攻撃の抑止にはHTTP リクエストが正規の Web サイトから発行されたものか検証する 必要がある
例えばhttps://www.abcbank.com という銀行のWebサイトがあったとして、/transferfund というエンドポイントへのGETリクエストでHTMLフォームを返却し、/transfer エンドポイントへのPOSTリクエストで挨拶を投稿するWebサイトを考えた時、
CSRF攻撃とは正規ユーザーであれば必ず経由するはずの/transferfund を迂回し、直接/transfer へリクエストを発行する…という性質がある
すなわち、/transferfund エンドポイントにアクセスした際に、ユーザーに対してセッション Cookie を割り当てた上で、CSRF 検証用のトークンを発行して HTML レスポンスで返却し、以降の HTTP リクエストに必ずトークンを含めた上でサーバーサイドで一致することを確認 すれば、リクエストが正規のWebサイトから発行されたものであることが検証できる
攻撃者は、攻撃対象のユーザーが/transferfund エンドポイントにアクセスした際に手に入れたトークンの内容を知ることができない(攻撃者自身がサイトにアクセスした場合は攻撃対象のユーザーとは別のセッションIDとトークンが返却される)ため、CSRF攻撃を成立させることができない
処理フローとしては以下のようになる

https://faun.pub/cross-site-request-forgery-protection-using-synchronizer-token-pattern-72b246ded56c より引用
実装にあたっては以下に留意する
CSRF tokens should not be transmitted using cookies. と言及されているSameSite=Strict が指定されていれば問題ないような気もするが…?REST APIにおける対処についても同様の手法でよく、クライアントサイドのJavaScriptにてHTMLに設定されたトークンを取得し、HTTPリクエストヘッダやペイロードに設定してサーバーサイドまで送信すればよい
より詳細な実装については、利用しているWebアプリケーションフレームワークのドキュメントを参照すべきだが、この時そのフレームワークが採用している手法がSynchronizer Token Pattern とDouble Submit Cookie Pattern のどちらであるかについては意識する必要がある
ちなみに、本手法についてCSRFトークンをセッション単位でなくHTTPリクエスト単位にローテーションする設計にすると、フォームの二重送信(フォームのSubmitボタン連打や、複数タブにて連続してSubmitをおこなった際に処理が多重に呼び出せてしまう問題)を抑止する機能としても活用できる
https://hiroga.hatenablog.com/entry/2017/10/04/080137
NTTデータが開発しているTERASOLUNAにおいてはトランザクショントークンチェック という名前付けがされていた
ドキュメントでは基本的にSynchronizer Token Patternの利用を推奨しているが、これはCSRF検証用トークンをセッションCookieに紐付けてサーバサイドにて管理する必要がある
この管理をおこなわずにCSRF対策をおこないたい場合にはDouble Submit Cookie Pattern が利用できる
本パターンでは、CSRF 検証トークンを Cookie に設定して Web ブラウザに返送および管理を委譲した上で、リクエスト時にCookie以外の配送経路(HTTPリクエストヘッダやペイロード)でトークンを取得し、サーバサイドでHTTP リクエスト上のトークン値と Cookie 上のトークン値の比較をおこない、リクエストが正規サイトから送信されたものであることを確認する
処理フローとしては以下のようになる

https://medium.com/@kaviru.mihisara/double-submit-cookie-pattern-820fc97e51f2 より引用
Synchronizer Token Patternではセッションで管理していたCSRFトークンをWebブラウザ上のCookieに保存していることから一見危険性が高いように感じられるが、なぜこの実装で問題ないかについて少し考えたい
CSRF検証トークンが持つべき性質として、以前の説明から以下があることがわかっている
1については、ユーザーのセッションCookieはCSRFとは別の脆弱性が無い限りには攻撃者は知ることができず、
2についてはセッションCookieと同様のライフサイクルで生成および送受信されるため問題がない
3に関しても、比較する片方の値はCookieにより配送されているが、もう片方の値をそれ以外の経路にて送信していることから検証をおこなうことができ、結果Synchronizer Token Patternと同等の仕様が満たせていることがわかる
注意点としては、Cookie は状況によって上書きすることができるため、攻撃者が何かしらの手法でユーザーに対して任意のCSRF検証トークンのCookieを強制できれば攻撃を成立させられてしまう
一例として、以下の記事で示されているように通信経路上に攻撃者が介在しているケースにおいては、攻撃者から任意のCookie値を強制されることからサイトを保護することはできない
Double Submit Cookieのアーキテクチャは通信内容が改ざんされないことを前提としているため、Synchronizer Token Patternと比較した際に固有の攻撃リスクが存在する
https://blog.tokumaru.org/2013/09/cookie-manipulation-is-possible-even-on-ssl.html
これについてはドキュメントにて対策が書かれており、サーバーサイドでのCSRFトークン発行時にユーザーのコンテキスト固有の値を含めて暗号化して配送した上で、Webブラウザから送られてきたトークンを復号化してから比較すれば、改ざんを検知することができる
こうすると、攻撃者が任意の検証トークン値を強制できたとしても、復号化後にユーザーのコンテキスト固有の値とマッチせず、攻撃を抑止することができる
To enhance the security of this solution include the token in an encrypted cookie - other than the authentication cookie (since they are often shared within subdomains) - and then at the server side match it (after decrypting the encrypted cookie) with the token in hidden form field or parameter/header for AJAX calls. This works because a sub domain has no way to over-write an properly crafted encrypted cookie without the necessary information such as encryption key.
例えば、DjangoではデフォルトではDouble Submit CookieパターンをベースにしたCSRF保護がおこなわれているが、前述した問題を考慮した実装がおこなわれている
https://docs.djangoproject.com/en/4.1/ref/csrf/#frequently-asked-questions
https://ops.jig-saw.com/tech-cate/django-react
以下の節で記載があるので興味があれば見てみるとよいが、基本的に前述のいずれかの対策を取ることが推奨される
対策への考え方がわかったところで、より現実的なケースについて考察する
最近のWebアプリケーションでよくある構成として、フロントエンドのアプリケーションをSPAとして作成した上で静的サイトとして配信しており、ページの初期ロード時のHTMLにCSRF検証トークンが存在しないケースが考えられる
これについては、ページの初期表示時にログイン状態の取得をおこなうエンドポイント等のHTTPレスポンス内でCSRFトークンを受け取って以降のリクエストで利用すればよい
また、Ajax通信中に発行されたCookieは以降のリクエストにおいては自動的に送信されるため、Double Submit Cookie Patternを利用している場合はリクエストの度にdocument.cookie の内容を確認し、トークンを送信すればよい
https://computer-technology.hateblo.jp/entry/20131226/p1
前項までに紹介してきたSameSite属性の登場やブラウザのデフォルト挙動の変化により、現在のCSRF攻撃はログイン後(≒ セッションCookie発行後)については成立させるのが難しくなってきている
むしろ、相対的にログインが必須でないページの方がCSRF攻撃のリスクが高まっている状況だが、これについてはプリセッション(認証を完了する前に事前発行するセッションCookie)を発行すべきだとしている
Djangoでは匿名セッション としてデフォルトで有効化されているなど、Webフレームワーク側で予め考慮されているケースも多い
例えばDouble Submit Cookie Pattern の項で紹介したフローでは/login へのPOSTが成功したタイミングでセッションCookieとCSRFトークンを発行しているが、これは/login ページのGETのタイミングでおこなったほうがCSRF抑止の観点では望ましい
ただその場合はセッション ID 固定化攻撃を避けるため、ログイン時にセッションIDの再発行を実施すべきであるとされる

https://medium.com/@kaviru.mihisara/double-submit-cookie-pattern-820fc97e51f2 より引用
ログインの概念がない問い合わせページのCSRF対策としては、CAPTCHAに代表されるようなユーザーがWebブラウザ上で特定の操作をおこなったことをサーバサイドで検証できるような仕組みを導入することで、リスクを緩和できる
https://ja.wikipedia.org/wiki/CAPTCHA
https://turningp.jp/network_and_security/public_form-csrf
GETエンドポイントに対してバックエンドでの更新操作をおこなわないのが原則となる
ただし、メールアドレスのアクティベーション やログアウト といった処理フローにおいて、ユーザーの利便性からGETエンドポイントでの更新操作を選択したくなるケースは存在するため、実装する場合はリスクについて留意する必要がある
メールアドレスのアクティベーションにおいては、一定時間で期限切れになる、第三者が推測不可能な認証コードを生成した後でURLの一部に設定し、リクエスト受付時に値のチェックをおこなうとよい
https://colo-ri.jp/develop/2012/07/web-how-to-mail-activation.html
なお、これはCSRF攻撃とは関係ないが、一部のブラウザやメールクライアントが処理速度改善を目的としてURL のプリフェッチをおこなう場合があるため、実装にあたってはURLアクセス時に直接バックエンドにリクエストが飛ぶようにはせず、JavaScript等を用いてページの初期ロード時に目的のエンドポイントへリクエストをおこなう設計にするとよい
この時エンドポイントをPOSTにするとCSRFトークンの検証をおこなうことができるのでより安全に実装できる
https://developer.mozilla.org/ja/docs/Web/HTTP/Link_prefetching_FAQ
ログアウトについては、一般的に/logout などのエンドポイントにGETでアクセスした際にそのままログアウトがおこなわれる場合が多いが、POSTを受け付けるエンドポイントを別途作成し、そちらでCSRFトークンをチェックするように変更するとよい
JWTを利用してセッション管理をおこなっているアプリケーションの場合、Authorization HTTPヘッダ等のCookie以外の配送経路を用いている場合はCSRF脆弱性を抑止することができる
以下の記事に詳しい
https://qiita.com/mejileben/items/ae7b346f0fc4a59101b5
まとめです
Synchronizer Token Pattern またはDouble Submit Cookie Pattern を導入しよう 🐂記事中で触れられなかったものの参考にした記事です 🙏
https://utkusen.medium.com/the-state-of-csrf-vulnerability-in-2022-3858e6d90ab9
https://www.ipa.go.jp/security/vuln/websecurity.html
https://qiita.com/mpyw/items/0595f07736cfa5b1f50c
バッジを受け取った著者にはZennから現金やAmazonギフトカードが還元されます。
Cross-Site Request Forgery Prevention Cheat Sheetは読むたびに内容が変更されているように思います。内容が安定しておらず、信頼性が低いと考えられます。
Double Submit Cookieについては、以前以下の記事で内容を批判しました。
https://blog.tokumaru.org/2018/11/csrf_26.html
これを読んでいただけるとわかると思いますが、Double Submit Cookieについては推進派と反対派が論争をしていて編集合戦の様相を呈しています。私は反対派です。
Cookieの改変については以下の記事および動画で解説をしています。
https://blog.tokumaru.org/2013/09/cookie-manipulation-is-possible-even-on-ssl.html
https://www.youtube.com/watch?v=GP1eEit1quY
サーバーサイドでのトークン発行時に値を暗号化して配送した上で、Webブラウザから送られてきたトークンを復号化してから比較すれば、改ざんを検知することができる
これは文字通り解釈すると間違いでしょう。攻撃者は自分用のトークンCookieを入手して、それを被害者のブラウザにセットしてやれば改ざんすることなく悪用が可能です。トークンにユーザIDなどを含める等しなければ、CSRF攻撃は検知できないと思います。
JWTはセッションCookieと同等の役割をする上、仕様上改ざん検知をおこなうことができるため、Cookieを用いたCSRFチェック全般が不要になる
JWTをCookieで受け渡しするサイトもあり、この場合はCSRF脆弱になる可能性があります。JWTだからCSRFセーフというわけではなく、トークンをリクエストヘッダで送信するからCSRF脆弱性の余地がないだけで、改ざん検知は関係ないと思います。
ご指摘および詳細に解説を頂きましてありがとうございます🙏
数日中に以下の方向で加筆・修正したいと思います
解答:CSRFの防止策に関するチートシートにツッコミを入れる はじめ頂いたリンクについて拝読致しました
OWASPのDouble Submit Cookieの説明を読んで漠然と不安を感じていたのですが、危険性のある具体的なケースを例示頂けたことでイメージが湧き大変ありがたかったです
一方で、(徳丸さんの他の記事でも紹介されていますように、)いくつかの著名なWebフレームワークでも現在採用されている手法であることと、本記事はアプリケーション開発者に向けた記事であり、利用しているWebフレームワークが採用している実装をそのまま利用することを推奨する趣旨であることから、Double Submit Cookie を使ってはならない…といった立場は取らないつもりで考えています
つきましては、以下について補足したいと思います
注意点としては、Cookie は状況によって上書きすることができるため、攻撃者が何かしらの手法でユーザーに対して~ の何かしらの手法 についてHTTPSを使ってもCookieの改変は防げないことを実験で試してみた の記事のケースを紹介し、その場合において Double Submit Cookie は Synchronizer Token Pattern と比較した際に固有の攻撃リスクがあるこちらについては、ご指摘頂いているトークンにユーザIDなどを含める等しなければ のニュアンスが漏れてしまっていることが問題であり、その旨を加筆する必要があると理解しました
検証用トークンについて、Cookieで配送する以上は外部から特定値を強制されるリスクがあるため、そのユーザーのコンテキスト(≒セッション)固有に発行されたものであることが復号化時に判断可能であることが必須要件である…という記載が必要であると考えています
私がJWTについて本番環境での利用経験がなく、特に認識が曖昧なまま書いてしまっていたものと思います
JWTをCSRF対策に活用できるのは配送経路にHTTPヘッダ等を利用した(Cookieを用いなかった)場合に限られることと、改ざん検知の下りは記載を削除します
(これについては、そもそも参考として貼り付けていた記事自体にもそのような注意喚起がされていたものでしたので、杜撰な記述をしてしまっていました…今後気をつけたいと思います)