Web を扱ってると「レスポンスヘッダー"ちゃんと"しましょう」ってやつはいつの時代でもあるものですが、それ系をあまりアウトプットしてなかったので書いておこうと思います。
ちなみに ASP.NET Core 3.1、動作確認した環境は個人的な興味のある Azure の WebApps (Windows)、WebApps(Linux) と Visual Studio 2019 でのローカルデバッグ(Kestrel)です。
不要なヘッダーの削除
ASP.NET Core 3.1 では(以前のバージョンから毎度のことですが)以下のような不要?かもしれない HTTP レスポンス の HTTP ヘッダー があります(←正しくはこう言うんですよねきっと)。
- Server: Kestrel
- X-Powered-By: ASP.NET
(ASP.NET Core になってから X-AspNet-Version なくなったかなー見てないから気にしてない)
こいつの削除から始めましょう。
事前に何も設定しない状態で、これら2つのヘッダー有無を確認した結果はこんな感じ。IIS がからまないやつでは X-Powered-By
が出ません。
実行環境 | Server | X-Powered-By |
---|---|---|
ローカルデバッグ(Kestrel) | 有 | 無 |
WebApps(Windows) | 有 | 有 |
WebApps(Linux) | 有 | 無 |
このヘッダーの削除の手順を2通りでやってみます。
設定: ヘッダーの削除 - Web.config 編
Web.config での制御は、ASP.NET 系あるあるな IIS の制御です。 プロジェクトにweb.config がない場合は、ファイルを追加して以下のように書けばよいです。既に web.config がありなんらかの設定をしている場合は、よしなに以下の設定を追加します。
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <security> <requestFiltering removeServerHeader="true" /> </security> <httpProtocol> <customHeaders> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol> </system.webServer> </configuration>
これで WebApps(Windows) で不要なヘッダーが消えました。
設定: ヘッダーの削除 - コードで制御編
web.config だと IIS 環境以外では意味がないので、Linux 環境用にコードで制御しておきます。
ASP.NET Core のプロジェクトの Program.cs で以下の 10-14行目のような感じにすればOKです。
WebApps(Linux) とローカルデバッグはもちろん不要なヘッダーが消えて想定通りの動作をします。
このコードの制御だけで IIS 環境の Server ヘッダーも消せるのかと思ったんですが消えないのですねー。リクエストパイプラインの流れ的に Kestrel 側で消しても IIS を通過するときにヘッダーがつけられてしまうからですかね。
ヘッダーを追加
Web サイトや API をセキュアにするためのヘッダーは色々あると思いますが、今回は以下のサイトで怒られないように設定することを目指します。
Analyse your HTTP response headers
NET Framework 時代だと web.config で制御してました。ASP.NET Core になっても同様に制御ができますが IIS 環境に縛られない ASP.NET Core ではミドルウェアで制御する方がベターでしょう。
ヘッダーの理解をしながら進めたかったので地道にヘッダーを一つずつ追加していきますが、最終的なオチとしては NuGet で良い感じに設定できるものが公開されていたりする(ブログの後半で紹介します)のでそれを使った方が楽かもです。
設定するヘッダー
ヘッダーの追加方法
ヘッダーの追加は、Startup.cs
の Configure
メソッドで以下のように書けばよいです。
app.Use(async (context, next) => { context.Response.Headers.Add("Header-Name", "Header-Value"); await next(); };
ASP.NET Core のミドルウェアは定義する順番がポイントになりますので、慣れてない方はこのブログの最後の参考に貼ったASP.NET Core のミドルウェアに関するドキュメントのリンクは要チェックです。
X-Content-Type-Options
Content-Tytpe
ヘッダーで指定した MIME タイプが正しいかをチェックしてくれるやつですね。ディレクティブは nosniff
です。
X-Content-Type-Options - HTTP | MDN
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
X-Frame-Options
ブラウザーが <frame>
, <iframe>
, <embed>
, <object>
の中でページを表示を許容するかですのヘッダーですね。
ディレクティブはこの2つ。
DENY
SAMEORIGIN
(ALLOW-FROM
は obsolete )
設定は、SAMEORIGIN` にするならこんな感じですね。
context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
X-XSS-Protection
クロスサイトスクリプティング (XSS) 攻撃を検出したときにページのロードをとめるかどうかです。
XSS を無効にする 0
、有効にする 1
でも、1
、1; mode=block
、1; report=<reporting-URI>
があります 。細かいことについては MDN を見ましょう(雑になってきた...)
context.Response.Headers.Add("X-XSS-Protection", "1");
Strict-Transport-Security (HSTS)
ウェブサイトへ HTTP のアクセスが来た時てそれを HTTPS にリダイレクトするようになった際のセキュリティです。
Strict-Transport-Security - HTTP | MDN
UI のない WebAPI を作ってるなら必要はなさそうですが、UI を追加した際にヘッダーのことを意識しなそうなので、最初からつけて設定しておいてもよいかもですね。
これは既に拡張メソッドが用意されているのでそれを呼ぶだけです。
app.UseHsts();
ドキュメントでどこに書くのかはチェックしておきましょう。また IncludeSubDomains とか MaxAge のオプションももちろんセットできるので必要に応じを設定しましょう。このリンクの上の方に書かれている HTTPS についてもセットで考えるところですね。
Referrer-Policy
リファラーの情報を送る制御です。全く送らない設定から、条件によって送る送らないって設定ができます。
設定例はこんな感じですね。
context.Response.Headers.Add("Referrer-Policy", "strict-origin-when-cross-origin");
Feature-Plicy
特定の Feature を使う/使わないの宣言をするポリシーです。カメラを使わないとかですが、その Feature の一覧はこちら。それっぽく設定してみました( Feature を全部定義した方がいいのかな...)。
context.Response.Headers.Add("Feature-Policy", "accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none';");
Content-Security-Policy (CSP)
いよいよ説明がめんどくさそうなやつですね...(だから最後にもってきた...)。ドキュメントを確認しましょう(雑。あとから見返したとき、これくらいのめんどくさそうなのは MDN を見返した方が安心安定なのでね..。
全てのコンテンツが自分自身からのみ取得する場合は以下でよいですが...。
context.Response.Headers.Add("Content-Security-Policy","default-src 'self'");
そんなシンプルなことはなかなかなくて、画像は Azure の blob storage やどっかの CDN からとかはあったりしますかね。ドキュメントをチェックして設定しましょう。
余談: ヘッダー追加の Tips
余談ですが、私は(他の人もやってると思いますが)他のミドルウェアを使っているとヘッダーの設定が重複してしまう可能性があります(重複すると Exception 吐いて死にます)。それはうざいので拡張メソッドとを作って...
public static class DictionaryExtensions { public static void TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, TValue value) { if (!source.ContainsKey(key)) source.Add(key, value); } }
こんな感じでヘッダーを追加してます。
context.Response.Headers.TryAdd("Content-Security-Policy","default-src 'self'");
設定後の動作確認
冒頭でAnalyse your HTTP response headersのスキャンしてレッドって流れを書いてたと思ったら消えてしまってる..まぁいいか
Analyse your HTTP response headers でスキャンしてみると...
グリーン!
WebApps(Windows) でも WebApps (Linux) でもほぼ同じにグリーンになります。
(グリーンすることが目的はなく、必要な設定を正しくすることが目的って意味では今回のサンプルの設定自体はあまり意味ないんですがねw)
NuGet で設定を楽に
NuGet で設定を楽にするようなものが公開されています。NWebsec は結構使われているパッケージだと思いますので使ってみるのもよいでしょう。
GitHub - NWebsec/NWebsec: Security libraries for ASP.NET
どんな NuGet を使おうとも、結局のところ何のヘッダーを何のために設定するのか理解しないといかんのですがね。
参考
このセキュアなヘッダーの設定とか何をするにも、まずはミドルウェアの理解が大事です。
結構古い記事ですが CSP の実装に関して参考になりそうなやつ。MS の Anthony さんの記事。