Go CSRF / XSRF Tokenの目的とフォームの保護 と標準パッケージのデフォルトのトークンの有効期限

by haturatu

4 min read

こんにちは、無能です。
個人的に作って個人的に使っているオンラインストレージ
GitHub - haturatu/puremania: No security, very fast, web UI self-hosted online storage
があるのですが、もし前段で認証を被せたいということがあるので認証プロキシを作ってみました。
GitHub - haturatu/auth-proxy: An authentication proxy server and frontend for a website without built-in authentication. JavaScript is supported, but it can also work without JS if using PHP-FPM. The backend is written in Go.
実際はほぼGeminiくんのおかげなのですが・・・。
認証が通るものだけプロキシします。
AdminユーザがAPI Endpoint用のBearerトークン発行してそのトークンを使ってリクエストが通るようにもなっています。しかし、この場合フロントからのセッションからもアクセスできないと行けない場合のAPIエンドポイントもあるので実際にはクッキー、Bearerトークンどちらでも通るようになってしまいます。
APIエンドポイントだけを保護する場合、Webのログインなどのフロントエンドをプライベートなネットワークに隔離してAPIエンドポイントだけを公開するようにすれば、ユーザ作成はプライベートネットワークから行ってそのユーザを使ってAPI Tokenの払い出しとAPI Proxyは実質的には可能です。 (API Gateway系のOSS使えばええやんというのはやめてください)
実際にはこうなるとクッキー、トークンでのダブルの総当りが実質的には可能になるのですが、まあ・・・。

そこで、また別のログイン時のウェブのフロントエンド安全のためにフォームのセキュリティに関してです。
JSロード必須にすればまあ問題はほとんど解決できる部分あるのですが、JSだけに頼るのもつまらないじゃないですか?というわけでJS以外の方法でフォームのセキュリティを高めるとしました。

CSRF / XSRF Token

これが今回の焦点になるのですが、これがそもそもなんなのかというところです。

簡単に言えば、サーバ側とクライアント側のリクエストがちゃんと俺のとこからアクセスして払い出したか、そしてそのトークンが有効期限内なら許可する、というものです。
例えば

<input type="hidden" name="xsrf_token" value="BeftikzaR8Oe6npnqXYC7WtBhuo:1760376550660">

このようなトークンがフロントエンド側HTML側に埋め込まれます。このvalueはサーバ側のシークレットキーによって署名されています。なのでサーバ側は俺のサーバ側から払い出したものだ!とわかります。
1760376550660はUNIX時間で文字の羅列があるだけですが現在時刻で何に必要かというと
xsrftoken package - golang.org/x/net/xsrftoken - Go Packages

func ValidFor(token, key, userID, actionID string, timeout time.Duration) bool

上記の期限設定に使われます。

実際にはtimeパッケージも入れて以下のようになります。

if !xsrftoken.ValidFor(clientToken, string(xsrfSecret), sessionID, r.URL.Path, 15*time.Minute) {

この引数を与えなかった場合のデフォルトは24時間なのでなかなか緩いですが、あくまでその場合はサーバが払い出したトークンである検証のみを行えればよいという本来あるべきXSRF Tokenの形としての提供のされ方かと思います。

CSRFとXSRFの違いってなんだよオ!

実際のところ、あんまりなさそうです。
目的とすれば、クロスサイトで攻撃を防止するものです。

CSRFの場合
GitHub - gorilla/csrf: Package gorilla/csrf provides Cross Site Request Forgery (CSRF) prevention middleware for Go web applications & services 🔒
このライブラリがあったのですが、たまたまここを見かけました。
gorilla/csrf CSRF脆弱性デモ

gorilla/csrf の実装を改善する方法の一つは、フォームで使用されるランダム値を、ユーザーのIDに紐付けられた暗号化トークンに置き換えることです。これにより、攻撃者が自身のCSRFトークンとCookie値を、攻撃対象のものとすり替えるのを防ぐことができます。なぜなら、攻撃者のCSRFトークンは別のIDに対応するからです。 実際、このメソッドは、x/net/xsrftokenHMAC を使用してユーザー ID、オプションのフォーム アクション、有効期限を認証するライブラリに実装されています。

それで標準ライブラリであるんだなあと思い
xsrftoken/xsrf.go
ソースを見てびっくり、コメント込でも100行!ちょっとSHA1まだ使っているのが心配なところはありますが、いずれ上がるのかな。
まあというわけで標準ライブラリがこれだけシンプルなら目的としていることはこれでよくて、SameSiteの検証を行うなら、gorilla/csrf使うべきというところでしょうか。

標準ライブラリの注意点

先程も触れましたがデフォルトのトークンの有効期限が24Hなことです。
実際の運用であればこのトークン期限だと悪用される可能性はあるのでもっと短い時間にしてもいいかもしれません。想定だと、このトークン自体はフォームを入力までのリクエストを送るということを想定すると24Hは同じトークンで何も他にセキュリティ的なものをいれていない場合何度もリクエストを通してしまうので無限curlで同じトークンで使いまわしでフォームにリクエストを送り総当りもできなくはありません。

PGP --- Contact --- Machines --- cat -v