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を使用するようにするにはどうすればよいですか?
開発ワークステーションでリリースビルドを実行する場合、これは役に立ちませんが、条件付きコンパイルは仕事をすることができます...
#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
いくつかの人々は、例を提供せずに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
ずっときれい!テストされていないコードが実際に機能する場合。
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);
}
}
}
RequireHttpsから派生するのは良いアプローチです。
問題を完全に回避するには、ローカルマシンで自己署名証明書を使用してIISを使用します。IISは組み込みWebサーバーよりも高速です、開発環境が本番に似ているという利点があります。
Scott Hanselmanには、VS2010およびIIS Express。 を使用してローカルHTTPSを実装するいくつかの方法に関する優れたリソースがあります。
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());
}
}
そもそも問題を引き起こしたのはASP.Net Development Serverだったため、Microsoftが IIS Express を持っていることに注意する価値があります。これはVisual Studioに付属しています(VS2010 SP1以降)。これは、IISの簡易バージョンですが、開発サーバーと同じくらい使いやすいですが、SSLを含むIIS 7.5の完全な機能セットをサポートしています。
Scott Hanselmanが IIS Express でSSLを操作する)に関する詳細な投稿をしています。
カスタム属性でRequireHttps属性を継承してください。次に、カスタム属性内で、現在のリクエストのIsLocalプロパティをチェックして、リクエストがローカルマシンから来ているかどうかを確認します。その場合、基本機能を適用しないでください。それ以外の場合は、基本操作を呼び出します。
これは私のために働いた、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));
}
});
}
本番および開発ワークステーションで使用できる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)) { %>
MVC 3では、ここにあるコードに基づいて独自のFilterProviderを追加しました: Global and Conditional Filters 特に、(ローカルユーザーのデバッグ情報を表示するなど)すべてのアクションをRequireHttpsAttribute
いつ HttpContext.Request.IsLocal == false
。
派生およびオーバーライドできる場合-それを行います。できない場合-MVCにはソースが付属しています。ソースを取得して、IsLocalをチェックする独自の[ForceHttps]属性を作成してください。
周りを調べた後、IIS ExpressとControllerクラスのOnAuthorizationメソッドのオーバーライド(Ref#1)でこの問題を解決できました。また、Hanselmanが推奨するルート(Ref #2)。ただし、次の2つの理由により、これら2つのソリューションに完全に満足することはできませんでした:1. Ref#1のOnAuthorizationは、アクションクラスでのみ機能し、コントローラークラスレベルでは機能しません。 Win7 SDK for makecert)、netshコマンド、およびポート80とポート443を使用するには、VS2010を管理者として起動する必要があります。
そこで、次の条件でシンプルさに焦点を当てたこのソリューションを思い付きました。
ControllerクラスまたはアクションレベルでRequireHttps属性を使用できるようにしたい
RequireHttps属性が存在する場合はMVCでHTTPSを使用し、存在しない場合はHTTPを使用する
管理者としてVisual Studioを実行する必要はありません。
IIS Expressによって割り当てられたHTTPおよびHTTPSポートを使用できるようにしたい(注#1を参照)
IIS Expressの自己署名SSL証明書を再利用できます。無効なSSLプロンプトが表示されてもかまいません
開発、テスト、およびプロダクションで、まったく同じコードベースと同じバイナリを使用し、可能な限り追加のセットアップ(たとえば、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>
Joelが述べたように、#if !DEBUG
ディレクティブを使用してコンパイルを変更できます。
Web.configファイルのコンパイル要素でDEBUGシンボルの値を変更できることがわかりました。お役に立てば幸いです。
MVC 6(ASP.NET Core 1.0):
適切な解決策は、env.IsProduction()またはenv.IsDevelopment()を使用することです。この回答の背後にある理由の詳細については、 運用環境でのみhttpsを要求する方法 を参照してください。
2つの異なるスタイルについての以下の簡潔な回答(上記のリンクを参照して、設計上の決定について詳しくお読みください):
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);
}
}
これは私にとって最もクリーンな方法でした。私の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());
}
...
}