ASP.NET CoreのCSRF対策周りのメモ

# .NET CoreのCSRF対策周りのメモ
CSRF対策回りのソースを読んだのでメモ

調べたバージョンはASP.NET Core 3.1

## DIコンテナに関連するクラスを登録する方法

`Startup.cs` で `services.AddMvc`や`services.AddControllersWithViews`を実行すると登録が実行される

順にソースを追いかけていくと
[MvcServiceCollectionExtensions](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Mvc/Mvc/src/MvcServiceCollectionExtensions.cs#L231)で[MvcViewFeaturesMvcCoreBuilderExtensions.AddViews](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs#L29) が実行される


その先では`AddViewServices` が実行され[ここ](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs#L144)で[AntiforgeryServiceCollectionExtensions.AddAntiforgery](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/AntiforgeryServiceCollectionExtensions.cs#L22)を呼び出しており、この中でDIコンテナへのクラスの登録が実行されている。

インターフェース自体が`internal` であるものも含まれており拡張の際は注意する必要がありそう。

## トークン発行
CSRFトークンの発行は `IAntiforgery` をインジェクションし `GetAndStoreTokens` メソッドを実行すると CSRF用のリクエストトークンとクッキートークンが発行され、クッキートークンはクッキーに保存される。
(`.cshtml` でもこの方法で実装されていた気がする...)

`GetTokens` メソッドはクッキートークンは生成されるが、クッキーへの保存は行わない。
(何のために使うんだろ?)

以下順を追ってみていく

### ソースのメモ
発行は実装クラスの[DefaultAntiforgery.GetAndStoreTokens](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultAntiforgery.cs#L41)で行う

`GetTokensInternal` メソッドを呼び出しその中では `GetCookieTokens` を呼び出しており
その結果を`Serialize` メソッドで加工している

クッキートークンが新しく生成されていれば、クッキーに保存している

そんな感じ...

#### GetCookieTokens メソッド
[ここ](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultAntiforgery.cs#L297)

`GetAntiforgeryFeature` はインスタンスの生成or再利用をしている感じかな？

そして`GetCookieTokenDoesNotThrow` を呼んでいる。この中では[DefaultAntiforgeryTokenStore .GetCookieToken](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultAntiforgeryTokenStore.cs#L27)を呼び出しており、(トークン取得後の２回目以降のリクエストなどで)`Cookie`にすでにクッキートークン が送信されていれば、それを取り出している

クッキートークンが送信されておりそれがトークン用の物として正しいものであれば再利用し、そうでなければ新しくクッキートークンの生成を行う
[このへん](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultAntiforgery.cs#L323)

このあとは `GetTokensInternal` で結果を受け取り、クッキートークンをもとにリクエストトークンを生成する。

リクエストトークンの生成は`IAntiforgeryTokenGenerator.GenerateRequestToken` メソッドで行う

#### IAntiforgeryTokenGenerator.GenerateRequestTokenメソッド
実装は[こちら](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultAntiforgeryTokenGenerator.cs#L36)

リクエストトークンを生成しているが構成要素はクッキートークンだけではなく、認証情報も含んでいる。

`GetAuthenticatedIdentity` で`ClaimsIdentity` を取得しており、取得できた場合は`IClaimUidExtractor.ExtractClaimUid` で認証済ユーザーを特定できるものをとってきているようだ。(後述)

もし`IAntiforgeryAdditionalDataProvider` があれば「追加データ」としてそれも構成要素に含めることができるようだ。（デフォルト実装は空文字列を返しているだけなので実質何もしていない）

#### IClaimUidExtractor.ExtractClaimUid メソッド
実装は[こちら](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultClaimUidExtractor.cs#L25)

ぱっと見た感じ`sub` というクレーム名や `ClaimTypes.NameIdentifier` や `ClaimTypes.Upn` のクレームが見つかればそれを利用しており それも見つからなければ認証済の全クレームを使っているのかな？

#### Serializeメソッド
[ここ](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultAntiforgery.cs#L417)

リクエストトークン、クッキートークンを加工し`AntiforgeryTokenSet` を作っているだけ

## トークンの検証
トークンの検証は`AutoValidateAntiforgeryTokenAttribute` や`ValidateAntiForgeryTokenAttribute`を適用することで検証が行われる。

いずれの場合も[ここ](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Mvc/Mvc.ViewFeatures/src/Filters/ValidateAntiforgeryTokenAuthorizationFilter.cs#L45)で`IAntiforgery.ValidateRequestAsync` が実行されることで検証が行われる。

以下順を追ってみていく

### ソースのメモ
検証は実装クラスの[DefaultAntiforgery.ValidateRequestAsync](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultAntiforgery.cs#L155)で行う

`IAntiforgeryTokenStore.GetRequestTokensAsync` でリクエストトークンとクッキートークンを取り出している。

クッキートークン、リクエストトークンともに無ければ例外を投げている。

そのあと`ValidateTokens` メソッドから `IAntiforgeryTokenGenerator.TryValidateTokenSet` を実行し検証

検証NGなら`AntiforgeryValidationException` をスローしている

#### IAntiforgeryTokenStore.GetRequestTokensAsync
実装は[こちら](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultAntiforgeryTokenStore.cs#L41)

`Cookie` からクッキートークンを リクエストヘッダーからリクエストトークンを取り出している。

フォームから取り出すこともある。

そして結果を`AntiforgeryTokenSet` として返す

#### IAntiforgeryTokenGenerator.TryValidateTokenSet
実装は[こちら](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultAntiforgeryTokenGenerator.cs#L110)

前半部分 [ここらへんまで](https://github.com/dotnet/aspnetcore/blob/v3.1.4/src/Antiforgery/src/Internal/DefaultAntiforgeryTokenGenerator.cs#L147)はリクエストトークンとクッキートークンが一致してるとかのチェックだと思う

そこから先は `HttpContext` ,リクエストトークン それぞれから取得した認証情報や追加データなどが一致しているかを検証している感じかな？

## ソースを読んでみて
リクエストトークンにはユーザーの認証関係の情報が含まれていることが確認できた。
このため認証前に取得したリクエストトークンが認証後に使用できないことも納得ができる。