ASP.NET MVC アプリの脆弱性対策について

対象

ASP.NET MVC 5 (.NET Framework) + Azure Web Apps を対象とする。

不要なレスポンスヘッダーの削除

既定のままだと、レスポンスヘッダーには以下のような ASP.NET を示唆する情報が出力される。

  • Server: Microsoft-IIS/10.0
  • X-AspNet-Version: 4.0.30319
  • X-AspNetMvc-Version: 5.2
  • X-Powered-By: ASP.NET

それぞれ、以下のように対応する。

  • X-AspNet-Version
  • X-Powered-By
  • Server

Web.config

<system.web>
  <!-- enableVersionHeader 属性を追加 -->
  <httpRuntime enableVersionHeader="false" targetFramework="4.6.1"/>
</system.web>

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <remove name="X-Powered-By"/>
    </customHeaders>
  </httpProtocol>

  <security>
    <requestFiltering removeServerHeader="true"/>
  </security>
</system.webServer>
  • X-AspNetMvc-Version
  • Server

Global.asax.cs

protected void Application_Start()
{
    MvcHandler.DisableMvcResponseHeader = true;
}

protected void Application_PreSendRequestHeaders()
{
    HttpContext.Current.Response.Headers.Remove("Server");
}

Server については、web.config と Global.asax.cs のどちらでも削除可能。 web.config の場合は VS がなぜか警告してくるが、VS の問題のようで実際の動作は問題ないらしい (はずだが、web.config の方だと削除できない時があった。自分の環境で有効かはちゃんと実際に確認する事)。

なお、Server の削除については、上記対応方法だと正確には正常レスポンス時しか削除されない (web.config と Global.asax.cs どちらも)。 例えば、FireFox などでリクエストヘッダーの Host の値を改変して再送するとエラーとなるが、その場合は Server が出力されてしまう。

IHttpModule を継承したモジュールを使用したり、Web.config で runAllManagedModulesForAllRequests 属性を使用するなどの方法はあるが、完全に削除できるようには見えないため、完全削除は諦めた方が無難と思われる。

特に後者の runAllManagedModulesForAllRequests 属性を使用する方法は、パイプラインの動きを変えて静的なファイルについてもパイプラインを通るようになってしまうため、この対策のためだけに使用する事はお勧めできない。

セキュリティに関するレスポンスヘッダーの追加

既定のままだと、セキュリティに関する以下のレスポンスヘッダーが出力されない。

  • X-Content-Type-Options
  • X-Frame-Options
  • X-XSS-Protection
  • Strict-Transport-Security
  • Content-Security-Policy
  • X-Download-Options

それぞれ、以下のように対応する。

Web.config

<system.webServer>
  <httpProtocol>
    <customHeaders>
        <add name="X-Content-Type-Options" value="nosniff"/>
        <add name="X-Frame-Options" value="SAMEORIGIN"/>
        <add name="X-XSS-Protection" value="1"/>
        <add name="Strict-Transport-Security" value="259200"/>

        <!-- CSP ヘッダーは、何を適用する必要があるのか把握した上で適切な設定を行う必要がある。インラインスクリプトの許可なども含まれるので、プロジェクト全体の実装方針に影響する -->
        <add name="Content-Security-Policy" value="default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; "/>

        <add name="X-Download-Options" value="noopen"/>
    </customHeaders>
  </httpProtocol>
</system.webServer>

追加ヘッダーの値についてはサイト毎に要求される内容も異なるため、どの設定値が適切かは検討する必要がある。上記はただの一例。

特に CSP ヘッダーは、何を適用する必要があるのか把握した上で適切な設定を行う必要がある。 インラインスクリプトの許可なども含まれるので、プロジェクト全体の実装方針に影響する。

上記は、CSS はインライン許可、スクリプトもインライン及び eval() 許可する場合の設定。 iframe で他サイトを読み込む場合は frame-src が必要だし、他にも色々種別がある。

なお、類似するヘッダーとして X-WebKit-CSP および X-Content-Security-Policy ヘッダーが存在するが、共に 2012 年前後の古いブラウザで使用されていたヘッダーなので、現在は不要。

CSRF について

CSRF は、MVC の AntiForgeryToken で対応する。メソッドは HttpPost が対象。Get はそもそも登録系の処理には使用しないので・・・。

注意点として、Html.AntiForgeryToken() を使用したページのレスポンスヘッダーには、MVC が自動で X-Frame-Options: SAMEORIGIN を追加する動作がある。 上述した web.config での X-Frame-Options ヘッダー追加を行っている場合、同ヘッダー出力が重複する場合があるので、web.config で常に出力する設定にしている場合は、AntiForgeryToken の自動出力は抑制しておく。

Global.asax.cs

protected void Application_Start()
{
    AntiForgeryConfig.SuppressXFrameOptionsHeader = true;
}

Session Fixation 問題について

ASP.NET のセッション情報を使用する限り、Session Fixation 問題への完全な対応は、難しい様子。 認証処理はセッションを使用するのではなく、Session Fixation 問題が発生しないフォーム認証を使って対応する。

クッキーのセキュア化

クッキーのセキュア化は、主に 2 つ。HttpOnly および Secure 属性の付与となる。

HttpOnly 属性は以下の設定を行う。

Web.config

<system.web>
  <httpCookies httpOnlyCookies="true"/>
</system.web>

Secure 属性の付与は、Global.asax.cs の Application_EndRequest とかで動的に設定した方が良いと思われる (特に問題なければ全クッキー一律に)。

localhost での実行時は、基本的に http での実行になるはずなので、一律に Secure 属性を適用すると、ローカルで実行できなくなる。 その為、ローカル以外での実行時に動的に適用した方が良いかと思われる。

なお、Secure 属性が適用されているかブラウザの F12 開発者ツールで確認する場合は、リクエストクッキーではなく、レスポンスクッキーを見る必要があるので、注意。