web-dev-qa-db-ja.com

ASP.NET MVC RequireHttpsは本番のみ

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を使用するようにするにはどうすればよいですか?

121
Zack Peterson

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

#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の提案に基づいてそれを書きました。 、およびランスフィッシャー。それが機能しない場合は、少なくとも一般的なアイデアを伝え、出発点を与える必要があります。

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

ずっときれい!テストされていないコードが実際に機能する場合。

129
Joel Mueller

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);
        }
    }
}
65
mikesl

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

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

Scott Hanselmanには、VS2010およびIIS Express。 を使用してローカルHTTPSを実装するいくつかの方法に関する優れたリソースがあります。

26
Lance Fisher

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());
      }
    }
11
gt124

そもそも問題を引き起こしたのはASP.Net Development Serverだったため、Microsoftが IIS Express を持っていることに注意する価値があります。これはVisual Studioに付属しています(VS2010 SP1以降)。これは、IISの簡易バージョンですが、開発サーバーと同じくらい使いやすいですが、SSLを含むIIS 7.5の完全な機能セットをサポートしています。

Scott Hanselmanが IIS Express でSSLを操作する)に関する詳細な投稿をしています。

10
Samuel Jack

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

9
spot

これは私のために働いた、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));
        }
    });
}
4
Eric Beijner

本番および開発ワークステーションで使用できる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);
            }
        }
    }
}

飾ることを忘れないでくださいLogOn AccountControllerのメソッド

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

そして、あなたのLogOnビューでこのようなものをhttps経由でフォームを投稿するために表示します。

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

MVC 3では、ここにあるコードに基づいて独自のFilterProviderを追加しました: Global and Conditional Filters 特に、(ローカルユーザーのデバッグ情報を表示するなど)すべてのアクションをRequireHttpsAttribute いつ HttpContext.Request.IsLocal == false

3
juhan_h

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

3
queen3

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

そこで、次の条件でシンプルさに焦点を当てたこのソリューションを思い付きました。

  1. ControllerクラスまたはアクションレベルでRequireHttps属性を使用できるようにしたい

  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クラスを作成し、この基本クラスからコントローラークラスを派生させます。ここまで読んだので、これらの方法を知っていると思います。ハッピーコーディング!

注#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
Leng Keng

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

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

2
Jose

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
Nick Niebling

これは私にとって最もクリーンな方法でした。私の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());   
        }
        ...
}
1
Carter Medlin