
2025/12/22 14:38
**トークンや隠しフォームフィールドなしで実装する CSRF 対策**
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Microdot は、11 月初旬にユーザーからの要望で追加された CSRF 保護機能を備えています。
最初は従来型の anti‑CSRF トークンを使用する予定でしたが、著者は
Sec‑Fetch‑Site ヘッダーに基づくモダンなアプローチを発見しました。このヘッダーは 2023 年 3 月以降リリースされたすべてのブラウザーでサポートされ、same-origin、same-site、cross-site、または none のいずれかの値を取ることができます。Microdot のロジックは、
Sec‑Fetch‑Site が cross-site に設定されたリクエストを拒否し、さらにデフォルトである allow_subdomains=False の場合には same-site リクエストもブロックします。Safari はこのヘッダーへの対応を 2023 年に追加しただけであり、古いブラウザーはそれを送信しないため、フォールバックが必要です。フォールバックでは、2019–2020 年から存在する
Origin ヘッダーを使用します。生のオリジンとホストを比較すると(スキームの違いやプロキシによる変更など)信頼性が低くなるため、Microdot は代わりに既存の CORS 設定に明示的に列挙されているオリジンのみを信用します。CORS で許可された任意のオリジンは自動的に CSRF チェックに受け入れられます。mkcert で知られる Filippo Valsorda は、Sec‑Fetch‑Site メソッドを先駆けし、それを Go の標準ライブラリに実装しました。OWASP は 12 月初旬に CSRF Prevention Cheat Sheet を更新して Sec‑Fetch‑Site を含めましたが、その後それを単独の解決策ではなく、ディフェンス・イン・デプスの一部として位置づけました。著者は OWASP の最終的な見解を監視し、必要に応じてダブルサブミットトークン保護へ戻す可能性があります。
実装詳細、ドキュメント、および例コードは既に Microdot プロジェクトで入手できます。
本文
数か月前、インターネット上のあるユーザーから私の小さなWebフレームワーク「Microdot」にCSRF保護を追加してほしいというリクエストが届きました。私はそれを素晴らしいアイデアだと感じました。
11 月初めにこの作業に着手した時、従来から使われているアンチ‑CSRFトークン、ダブル・サブミット・クッキー、隠しフォームフィールドなどの要素を扱うことになると予想していました。私はそのやや面倒な道を歩み始めましたが、その後、CSRF攻撃に対処するための新しい方法に出会い、はるかにシンプルであることに気付きました。それについて以下で説明します。
セキュリティ機能の実装
よく共有されるアドバイスとして、「自分でセキュリティ機能を実装してはいけない」というものがあります。代わりに、日々セキュリティを考えている専門家が構築した確立されたソリューションを探すべきです。
Microdot のメイン(唯一)の保守者として、私は既存のエコシステムから利用できる解決策を持っていません。外部からの貢献は歓迎していますが、フレームワークの大部分はゼロから自分で構築してきました。そのため、CSRF 保護コードを書かなければ機能は実装されずに終わります。
セキュリティ機能を開発する最初のステップは、OWASP が何と言っているかを確認することです。11 月初め、私は OWASP の CSRF Prevention Cheat Sheet を開き、CSRF 保護の世界で新しくて興味深いものがあるかどうか調べましたが、特に大きな変更は見当たりませんでした。OWASP によると、当時最良の CSRF 防御はまだアンチ‑CSRF トークンを中心に構築されていたため、Microdot でそれを実装しようとしました。
(CSRF) フォースにおける混乱
実装を進めている途中、12 月初旬に Flask のリポジトリで「モダン」な CSRF 保護を追加する提案が出ました。OWASP にも言及されていない新しい CSRF 防御手段はどこから来るのでしょうか?
この疑問は、Go と Ruby コミュニティのブログ投稿や議論へと私を導き、さらに OWASP の GitHub リポジトリ自体で長い議論が行われました。数週間後にプルリクエストが提出され、CSRF Cheat Sheet にこの手法が追加されました。
モダン CSRF 保護
「モダン」と呼ばれる方法は Sec‑Fetch‑Site ヘッダーを利用しています。このヘッダーはすべての最新デスクトップとモバイルブラウザがサーバーへ送信するリクエストに含めます。Mozilla によれば、2023 年 3 月以降にリリースされた全ブラウザがこのヘッダーをサポートしています。
Sec-Fetch-Site は次の 4 つの値を取ることがあります:
:ターゲットサーバーと同じオリジンから来たリクエストsame-origin
:同じサイトだが別ドメインから来たリクエストsame-site
:関連のないオリジンから来たリクエストcross-site
:ユーザー操作(例: リンクをクリック)によって開始されたリクエストnone
JavaScript でこのヘッダーを設定できないため、サーバーはヘッダーが存在すれば、その値を信頼してブラウザからのリクエストだとみなせます。したがって
Sec-Fetch-Site が cross-site の場合にリクエストを拒否することで、CSRF を防ぐことができます。
この手法を知った私はトークンベースの実装を中断し、数時間でモダンなアプローチを実装しました。詳細は以下です:
-
サブドメイン
いくつかの場合では、同じ登録済みドメインを共有するサブドメインが独立して動作します。サーバーは
がSec-Fetch-Site
のリクエストを拒否したい場合があります。Microdot にsame-site
引数を追加し、デフォルトでは False(サブドメインのリクエストをブロック)に設定しました。allow_subdomains -
ブラウザサポート
ほとんどのブラウザは 2019〜2021 年間でこの機能を実装しましたが、Safari は 2023 年に追加されました。ヘッダーが無いリクエストすべてを拒否すると古いデバイスや非ブラウザクライアントが壊れてしまうため、代替策として
ヘッダーをフォールバックに使用するのが一般的です。Origin -
Origin ヘッダーの利用
をOrigin
と比較することは簡単ではありません。なぜならHost
はリバースプロキシで変更される可能性があり、スキームを含まないためです。より直接的な方法として、期待されるオリジン名をユーザーに明示的に設定してもらうことです。Microdot では既存の CORS サポートと連携し、許可されたオリジンのリストを管理しています。Host -
今後の作業
現時点ではホストヘッダーのチェックは追加していませんが、将来的に検討する可能性があります。
Go エコシステムで活躍するセキュリティ開発者 Filippo Valsorda(
mkcert の作者)が、この手法について詳細を記したブログ投稿を書いています。彼は最初にこのアイデアを提案し、Go 標準ライブラリでも実装していますので、ぜひご覧ください。
OWASP を再検討
OWASP CSRF Prevention Cheat Sheet は 12 月初めに
Sec-Fetch-Site を含むよう更新されました。最初は防御の一環としてのみ記載されていましたが、これはやや奇妙でした。OWASP の GitHub リポジトリでの議論を経て、この手法をトークンベースのアプローチに代わるオプションとして追加するプルリクエストが作られました。その後、再びダウングレードされました。
これは単なる誤解だと願っています。OWASP が最終的な立場をどこに置くかは注視していますが、もし防御の一環として残る場合でも、私はほぼ完成したダブル・サブミット型アンチ‑CSRF トークン実装をプロジェクトに組み込む準備ができています。
結論
オープンソースで働く際に最も好きな点は、すべての作業が公開されていることです。永続的な記録が検索・レビュー可能だからです。私の CSRF 保護の旅は、暗号化とクッキーのやや面倒な演習から始まりましたが、予期せぬヒントのおかげで楽しくエキサイティングな学びの機会へと変わりました。
ブログを読んでいただきありがとうございます!この記事がお役に立ったら、Buy Me a Coffee で一度だけのご寄付で私をカフェイン補給していただけると嬉しいです。どうぞよろしくお願いいたします。