.NET CoreのCSRF対策周りのメモ
CSRF対策回りのソースを読んだのでメモ
調べたバージョンはASP.NET Core 3.1
DIコンテナに関連するクラスを登録する方法
Startup.cs
で services.AddMvc
やservices.AddControllersWithViews
を実行すると登録が実行される
順にソースを追いかけていくと
MvcServiceCollectionExtensionsでMvcViewFeaturesMvcCoreBuilderExtensions.AddViews が実行される
その先ではAddViewServices
が実行されここでAntiforgeryServiceCollectionExtensions.AddAntiforgeryを呼び出しており、この中でDIコンテナへのクラスの登録が実行されている。
インターフェース自体がinternal
であるものも含まれており拡張の際は注意する必要がありそう。
トークン発行
CSRFトークンの発行は IAntiforgery
をインジェクションし GetAndStoreTokens
メソッドを実行すると CSRF用のリクエストトークンとクッキートークンが発行され、クッキートークンはクッキーに保存される。
(.cshtml
でもこの方法で実装されていた気がする...)
GetTokens
メソッドはクッキートークンは生成されるが、クッキーへの保存は行わない。
(何のために使うんだろ?)
以下順を追ってみていく
ソースのメモ
発行は実装クラスのDefaultAntiforgery.GetAndStoreTokensで行う
GetTokensInternal
メソッドを呼び出しその中では GetCookieTokens
を呼び出しており
その結果をSerialize
メソッドで加工している
クッキートークンが新しく生成されていれば、クッキーに保存している
そんな感じ...
GetCookieTokens メソッド
GetAntiforgeryFeature
はインスタンスの生成or再利用をしている感じかな?
そしてGetCookieTokenDoesNotThrow
を呼んでいる。この中ではDefaultAntiforgeryTokenStore .GetCookieTokenを呼び出しており、(トークン取得後の2回目以降のリクエストなどで)Cookie
にすでにクッキートークン が送信されていれば、それを取り出している
クッキートークンが送信されておりそれがトークン用の物として正しいものであれば再利用し、そうでなければ新しくクッキートークンの生成を行う
このへん
このあとは GetTokensInternal
で結果を受け取り、クッキートークンをもとにリクエストトークンを生成する。
リクエストトークンの生成はIAntiforgeryTokenGenerator.GenerateRequestToken
メソッドで行う
IAntiforgeryTokenGenerator.GenerateRequestTokenメソッド
実装はこちら
リクエストトークンを生成しているが構成要素はクッキートークンだけではなく、認証情報も含んでいる。
GetAuthenticatedIdentity
でClaimsIdentity
を取得しており、取得できた場合はIClaimUidExtractor.ExtractClaimUid
で認証済ユーザーを特定できるものをとってきているようだ。(後述)
もしIAntiforgeryAdditionalDataProvider
があれば「追加データ」としてそれも構成要素に含めることができるようだ。(デフォルト実装は空文字列を返しているだけなので実質何もしていない)
IClaimUidExtractor.ExtractClaimUid メソッド
実装はこちら
ぱっと見た感じsub
というクレーム名や ClaimTypes.NameIdentifier
や ClaimTypes.Upn
のクレームが見つかればそれを利用しており それも見つからなければ認証済の全クレームを使っているのかな?
Serializeメソッド
リクエストトークン、クッキートークンを加工しAntiforgeryTokenSet
を作っているだけ
トークンの検証
トークンの検証はAutoValidateAntiforgeryTokenAttribute
やValidateAntiForgeryTokenAttribute
を適用することで検証が行われる。
いずれの場合もここでIAntiforgery.ValidateRequestAsync
が実行されることで検証が行われる。
以下順を追ってみていく
ソースのメモ
検証は実装クラスのDefaultAntiforgery.ValidateRequestAsyncで行う
IAntiforgeryTokenStore.GetRequestTokensAsync
でリクエストトークンとクッキートークンを取り出している。
クッキートークン、リクエストトークンともに無ければ例外を投げている。
そのあとValidateTokens
メソッドから IAntiforgeryTokenGenerator.TryValidateTokenSet
を実行し検証
検証NGならAntiforgeryValidationException
をスローしている
IAntiforgeryTokenStore.GetRequestTokensAsync
実装はこちら
Cookie
からクッキートークンを リクエストヘッダーからリクエストトークンを取り出している。
フォームから取り出すこともある。
そして結果をAntiforgeryTokenSet
として返す
IAntiforgeryTokenGenerator.TryValidateTokenSet
実装はこちら
前半部分 ここらへんまではリクエストトークンとクッキートークンが一致してるとかのチェックだと思う
そこから先は HttpContext
,リクエストトークン それぞれから取得した認証情報や追加データなどが一致しているかを検証している感じかな?
ソースを読んでみて
リクエストトークンにはユーザーの認証関係の情報が含まれていることが確認できた。
このため認証前に取得したリクエストトークンが認証後に使用できないことも納得ができる。