ASP.NET MVCは本番環境でのみHttpsを必要とする


121

RequireHttpsAttributeを使用して、セキュリティで保護されていないHTTPリクエストがアクションメソッドに送信されないようにします。

C#

[RequireHttps] //apply to all actions in controller
public class SomeController 
{
    [RequireHttps] //apply to this action only
    public ActionResult SomeAction()
    {
        ...
    }
}

VB

<RequireHttps()> _
Public Class SomeController

    <RequireHttps()> _
    Public Function SomeAction() As ActionResult
        ...
    End Function

End Class

残念ながら、ASP.NET開発サーバーはHTTPSをサポートしていません。

ASP.NET MVCアプリケーションを運用環境に公開するときにRequireHttpsを使用し、ASP.NET開発サーバー上の開発ワークステーションで実行する場合はどうすればよいですか?


3
ローカルIISおよびIIS Expressでテストします。私のSSLのブログを参照してくださいblogs.msdn.com/b/rickandy/archive/2011/04/22/...blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT

回答:


129

これは、開発ワークステーションでリリースビルドを実行する場合には役立ちませんが、条件付きコンパイルで実行できます...

#if !DEBUG
[RequireHttps] //apply to all actions in controller
#endif
public class SomeController 
{
    //... or ...
#if !DEBUG
    [RequireHttps] //apply to this action only
#endif
    public ActionResult SomeAction()
    {
    }

}

更新

Visual Basicでは、属性は技術的には、属性が適用される定義と同じ行の一部です。条件付きコンパイルステートメントを行内に置くことはできないため、関数宣言を2回記述する必要があります。1回は属性付きで、もう1回は属性なしです。ただし、醜さを気にしない場合は機能します。

#If Not Debug Then
    <RequireHttps()> _
    Function SomeAction() As ActionResult
#Else
    Function SomeAction() As ActionResult
#End If
        ...
    End Function

アップデート2

いくつかの人々がRequireHttpsAttribute例を提供せずに由来することを述べたので、ここにあなたのためのものがあります。このアプローチは、条件付きコンパイルのアプローチよりもはるかにクリーンだと思います。あなたの立場での私の好みでしょう。

免責事項:私はこのコードを少しでもテストしていません。私のVBはかなり錆びています。私が知っているのは、それがコンパイルされるということだけです。私は、spot、queen3、およびLance Fisherの提案に基づいてそれを書きました。それが機能しない場合は、少なくとも一般的な考えを伝え、開始点を提供する必要があります。

Public Class RemoteRequireHttpsAttribute
    Inherits System.Web.Mvc.RequireHttpsAttribute

    Public Overrides Sub OnAuthorization(ByVal filterContext As  _
                                         System.Web.Mvc.AuthorizationContext)
        If IsNothing(filterContext) Then
            Throw New ArgumentNullException("filterContext")
        End If

        If Not IsNothing(filterContext.HttpContext) AndAlso _
            filterContext.HttpContext.Request.IsLocal Then
            Return
        End If

        MyBase.OnAuthorization(filterContext)
    End Sub

End Class

基本的に、現在のリクエストがローカルである場合(つまり、localhostを介してサイトにアクセスしている場合)、新しい属性はデフォルトのSSL認証コードを実行する代わりに終了します。次のように使用できます。

<RemoteRequireHttps()> _
Public Class SomeController

    <RemoteRequireHttps()> _
    Public Function SomeAction() As ActionResult
        ...
    End Function

End Class

ずっときれい!テストされていないコードが実際に機能することを条件とします。


ありがとう、ええと、私の投稿を編集してくれた、ザック。あなたの質問はC#にあったので、C#応答を投稿しました。VBが適切であることを知りませんでした。誰もが条件付きコンパイルを使用してVBの属性を制御する方法があるかどうかを知っていますか、それとも不可能ですか?
Joel Mueller、

はい、それはC#で機能し、VBでも機能しますが、関数/クラス定義のかなり醜い複製を行う必要があります。上記の私の更新された回答を参照してください。
Joel Mueller、

ごめんなさい。VBコードサンプルは、入手がますます難しくなっています。それが問題になるとは思わなかった。元の質問を更新しました。C#では、属性に関する条件付きコンパイルは確実に機能しますか?私はテストしていません。それは完璧でエレガントなソリューションのようです。
ザックピーターソン

RemoteRequireHttpsAttributeコードは完全に機能します。これは、条件付きコンパイルよりもVBの方がはるかにエレガントです。ジョエル、ありがとう。
ザックピーターソン

2
ありがとう、これがまさに私が必要としていたことだった。乾杯!
davecoulter '05

65

C#バージョンが必要な場合:

using System;
using System.Web.Mvc;

namespace My.Utils
{
    public class MyRequireHttpsAttribute : RequireHttpsAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (filterContext.HttpContext != null && filterContext.HttpContext.Request.IsLocal)
            {
                return;
            }

            base.OnAuthorization(filterContext);
        }
    }
}

セキュリティ対策としてこれこれを読んだら追加する必要filters.Add(new MyRequireHttpsAttribute ());がありFilterConfigますか?
shaijut 2016

この回答に基づいて、Startup.csのフィルターまたはコントローラーの属性スタイルを使用してMVC 6のソリューションを作成しました。
Nick Niebling 16

26

RequireHttpsから派生することは良いアプローチです。

問題を完全に回避するには、自己署名証明書を使用してローカルマシンでIISを使用することもできます。IISは組み込みのWebサーバーよりも高速であり、開発環境が本番環境に近いという利点があります。

Scott Hanselmanは、VS2010とIIS ExpressでローカルHTTPSを実装するいくつかの方法に関する優れたリソースを持っています。


ya-Mifi wifi Verizonデバイスでポート転送を行おうとし、ポート443を転送できないことが判明するまで!!! #*&#*&$
Simon_Weaver

ローカルマシンでIISを自己署名証明書と共に使用することについて私が気に入らないのは、変更をテストするために追加の展開手順を実行する必要があることです。セキュリティに関連する何かをテストするのは理にかなっていると思いますが、他の小さな変更を確認するだけの場合、CassiniがHTTPSをサポートできないことを回避するためだけにデプロイしなければならないのは面倒です。
davecoulter

1
@davecoulter-クライアントバージョンのWindowsでIISエクスプレスを使用します。cassiniは必要ありません。ssl機能を含め、IISとまったく同じように機能します。
エリックファンケンブッシュ

@Mystere Man-ええ、私はそのコメント以来それを見つけました。ヒントをありがとう:)
davecoulter '26 / 08/26

そのようなことを行う方法についての詳細情報またはリンクを追加する必要があります。
stephenbayer 2014

12

MVCフィルターシステムとGlobal.asax.csを活用して、これを実行できると思います...

    protected void Application_Start()
    {
      RegisterGlobalFilters(GlobalFilters.Filters);
    }

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
      filters.Add(new HandleErrorAttribute());
      if(Config.IsProduction) //Some flag that you can tell if you are in your production environment.
      {
        filters.Add(new RequireHttpsAttribute());
      }
    }

この回答は、すべてのリクエストで実行される新しいフィルターを実装するのではなく、アプリケーションの有効期間ごとに1つのチェックが含まれるため、この回答を好みます。
Abdulhameed

10

そもそも問題の原因となったのはASP.Net開発サーバーだったので、MicrosoftがIIS Expressを持っていることは注目に値します。これはVisual Studioに同梱されています(VS2010 SP1以降)。これは、開発サーバーと同じくらい使いやすいIISの縮小バージョンですが、SSLを含むIIS 7.5の完全な機能セットをサポートしています。

Scott Hanselmanが、IIS ExpressでのSSLの使用に関する詳細な記事を公開しています


9

カスタム属性のRequireHttps属性を継承するのはどうですか。次に、カスタム属性内で、現在のリクエストのIsLocalプロパティをチェックして、リクエストがローカルマシンからのものかどうかを確認します。その場合は、基本機能を適用しないでください。それ以外の場合は、基本操作を呼び出します。


4

これは、MVC 6(ASP.NET Core 1.0)でうまくいきました。コードはデバッグが開発中であるかどうかをチェックし、そうでない場合はsslは必要ありません。すべての編集はStartup.csにあります。

追加:

private IHostingEnvironment CurrentEnvironment { get; set; }

追加:

public Startup(IHostingEnvironment env)
{
    CurrentEnvironment = env;
}

編集:

public void ConfigureServices(IServiceCollection services)
{
    // additional services...

    services.AddMvc(options =>
    {
        if (!CurrentEnvironment.IsDevelopment())
        {
            options.Filters.Add(typeof(RequireHttpsAttribute));
        }
    });
}

3

導出してオーバーライドできる場合は、それを実行してください。できない場合-MVCにはソースが付属しているので、ソースを取得して、IsLocalをチェックする独自の[ForceHttps]属性を作成します。


3

MVC 3の場合は、独自のFilterProviderを追加しました(ここにあるコードに基づいて:グローバルフィルターと条件付きフィルター、特にローカルユーザーのデバッグ情報の表示など)は、RequireHttpsAttributewhenですべてのアクションを装飾しますHttpContext.Request.IsLocal == false


または、リクエストがローカルである場合に、条件付きでグローバルフィルターコレクションに追加することもできます。アプリがすぐに起動するように設定されている場合は、リクエストが利用できない可能性があるため、try / catchブロックでこれを確認する必要があることに注意してください。
tvanfosson

3

問題を調査した後、IIS ExpressとコントローラークラスのOnAuthorizationメソッドのオーバーライドでこの問題を解決することができました(参照#1)。私はまた、ハンセルマンが推奨するルートを使用しました(参照番号2)。ただし、次の2つの理由により、これら2つのソリューションに完全に満足できませんでした。1. Ref#1のOnAuthorizationは、アクションレベルでのみ機能し、コントローラークラスレベルでは機能しません。 )、netshコマンド、そしてポート80とポート443を使用するには、VS2010を管理者として起動する必要があります。

そこで、次の条件でシンプルさを重視するこのソリューションを思いつきました。

  1. コントローラークラスまたはアクションレベルでRequireHttps attbbuteを使用できるようにしたい

  2. RequireHttps属性が存在する場合はMVCでHTTPSを使用し、存在しない場合はHTTPを使用したい

  3. 管理者としてVisual Studioを実行する必要がない

  4. IIS Expressによって割り当てられたHTTPおよびHTTPSポートを使用できるようにしたい(注1を参照)

  5. IIS Expressの自己署名SSL証明書を再利用できますが、無効なSSLプロンプトが表示されても問題ありません

  6. 開発、テスト、および本番環境で、まったく同じコードベースと同じバイナリを使用し、追加のセットアップ(たとえば、netsh、mmc certスナップインなど)からできるだけ独立させたい

今、背景と説明が邪魔にならないように、このコードが誰かを助け、時間を節約できることを願っています。基本的に、Controllerから継承するBaseControllerクラスを作成し、この基本クラスからコントローラークラスを派生させます。ここまで読んだので、これらの方法を知っていると思います。だから、幸せなコーディング!

Note#1:これは、便利な関数「getConfig」を使用することで実現されます(コードを参照)。

Ref#1:http : //puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html

Ref#2:http : //www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx

========== BaseControllerのコード===================

     #region Override to reroute to non-SSL port if controller action does not have RequireHttps attribute to save on CPU 
    // By L. Keng, 2012/08/27
    // Note that this code works with RequireHttps at the controller class or action level.
    // Credit: Various stackoverflow.com posts and http://puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html
    protected override void OnAuthorization(AuthorizationContext filterContext)
    {
        // if the controller class or the action has RequireHttps attribute
        var requireHttps = (filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Count() > 0 
                            || filterContext.ActionDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Count() > 0);
        if (Request.IsSecureConnection)
        {
            // If request has a secure connection but we don't need SSL, and we are not on a child action   
            if (!requireHttps && !filterContext.IsChildAction)
            {
                var uriBuilder = new UriBuilder(Request.Url)
                {
                    Scheme = "http",
                    Port = int.Parse(getConfig("HttpPort", "80")) // grab from config; default to port 80
                };
                filterContext.Result = this.Redirect(uriBuilder.Uri.AbsoluteUri);
            }
        }
        else
        {
            // If request does not have a secure connection but we need SSL, and we are not on a child action   
            if (requireHttps && !filterContext.IsChildAction)
            {
                var uriBuilder = new UriBuilder(Request.Url)
                {
                    Scheme = "https",
                    Port = int.Parse(getConfig("HttpsPort", "443")) // grab from config; default to port 443
                };
                filterContext.Result = this.Redirect(uriBuilder.Uri.AbsoluteUri);
            }
        }
        base.OnAuthorization(filterContext);
    }
    #endregion

    // a useful helper function to get appSettings value; allow caller to specify a default value if one cannot be found
    internal static string getConfig(string name, string defaultValue = null)
    {
        var val = System.Configuration.ConfigurationManager.AppSettings[name];
        return (val == null ? defaultValue : val);
    }

==============終了コード================

Web.Release.Configに次のコードを追加して、HttpPortとHttpsPortをクリアします(デフォルトの80と443を使用します)。

<appSettings>
<add key="HttpPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="HttpsPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
</appSettings>

3

本番環境と開発ワークステーションで使用できる1つのソリューション。これは、web.configのアプリケーション設定のオプションに基づいています

<appSettings>
     <!--Use SSL port 44300 in IIS Express on development workstation-->
     <add key="UseSSL" value="44300" />
</appSettings>

SSLを使用しない場合は、キーを削除します。標準SSLポート443を使用する場合は、値を削除するか、443を指定します。

次に、条件を処理するRequireHttpsAttributeのカスタム実装を使用します。それは実際に由来しています RequireHttpsし、条件を追加することを除いて、基本メソッドと同じ実装を使用します。

public class RequireHttpsConditional : RequireHttpsAttribute
{
    protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
    {
        var useSslConfig = ConfigurationManager.AppSettings["UseSSL"];
        if (useSslConfig != null)
        {
            if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            {
                throw new InvalidOperationException("The requested resource can only be accessed via SSL.");
            }

            var request = filterContext.HttpContext.Request;
            string url = null;
            int sslPort;

            if (Int32.TryParse(useSslConfig, out sslPort) && sslPort > 0)
            {
                url = "https://" + request.Url.Host + request.RawUrl;

                if (sslPort != 443)
                {
                    var builder = new UriBuilder(url) {Port = sslPort};
                    url = builder.Uri.ToString();
                }
            }

            if (sslPort != request.Url.Port)
            {
                filterContext.Result = new RedirectResult(url);
            }
        }
    }
}

ログオンを装飾することを忘れないでくださいメソッド

[RequireHttpsConditional]
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)

https経由でフォームを投稿するために、ログオンビューでこのようなものを使用します。

<% using (Html.BeginFormSecure("LogOn", "Account", new { ReturnUrl = Request.QueryString["ReturnUrl"] }, Request.IsSecureConnection, Request.Url)) { %>

このエラーが発生します:XMLHttpRequestがm.XXX.com/Auth/SignInを読み込めません。要求されたリソースに「Access-Control-Allow-Origin」ヘッダーがありません。したがって、オリジン ' m.XXX.com 'はアクセスを許可されていません。
Ranjith Kumar Nagiri 2014年

2

Joelが述べたように、#if !DEBUGディレクティブを使用してコンパイルを変更できます。

web.configファイルのコンパイル要素のDEBUGシンボルの値を変更できることがわかりました。お役に立てば幸いです。


1

MVC 6(ASP.NET Core 1.0):

適切な解決策は、env.IsProduction()またはenv.IsDevelopment()を使用することです。この回答の背後にある理由について詳しくは、本番環境でのみhttpsを要求する方法をご覧ください。

2つの異なるスタイルについて、以下の要約された回答(設計の決定についての詳細は上記のリンクを参照):

  1. Startup.cs-フィルターの登録
  2. BaseController-属性スタイル

Startup.cs(フィルターの登録):

public void ConfigureServices(IServiceCollection services)
{
    // TODO: Register other services

    services.AddMvc(options =>
    {
        options.Filters.Add(typeof(RequireHttpsInProductionAttribute));
    });
}

BaseController.cs(属性スタイル):

[RequireHttpsInProductionAttribute]
public class BaseController : Controller
{
    // Maybe you have other shared controller logic..
}

public class HomeController : BaseController
{
    // Add endpoints (GET / POST) for Home controller
}

RequireHttpsInProductionAttribute:上記の両方がRequireHttpsAttributeから継承するカスタム属性を使用しています:

public class RequireHttpsInProductionAttribute : RequireHttpsAttribute
{
    private bool IsProduction { get; }

    public RequireHttpsInProductionAttribute(IHostingEnvironment environment)
    {
        if (environment == null)
            throw new ArgumentNullException(nameof(environment));
        this.IsProduction = environment.IsProduction(); 
    }
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (this.IsProduction)
            base.OnAuthorization(filterContext);
    }
    protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
    {
        if(this.IsProduction)
            base.HandleNonHttpsRequest(filterContext);
    }
}

1

これは私にとって最もきれいな方法でした。私のApp_Start\FilterConfig.csファイルで。ただし、リリースビルドはもう実行できません。

... 
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
        if (!Web.HttpContext.Current.IsDebuggingEnabled) {
            filters.Add(new RequireHttpsAttribute());   
        }
        ...
}

または、カスタムエラーページがオンのときにhttpsのみを要求するように設定することもできます。

... 
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
        if (Web.HttpContext.Current.IsCustomErrorEnabled) {
            filters.Add(new RequireHttpsAttribute());   
        }
        ...
}

これは、MVC 5でうまく機能する簡単なソリューションです:)
MWD

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.