ASP.NET MVC RequireHttps in Production Only

前端 未结 15 1858
花落未央
花落未央 2020-11-28 18:23

I want to use the RequireHttpsAttribute to prevent unsecured HTTP requests from being sent to an action method.

C#

[RequireHttps] //apply to all acti         


        
相关标签:
15条回答
  • 2020-11-28 19:00

    This was the cleanest way for me. In my App_Start\FilterConfig.cs file. Can't run release builds anymore though.

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

    Alternatively, you could set it to only require https when your custom error page is on.

    ... 
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
            if (Web.HttpContext.Current.IsCustomErrorEnabled) {
                filters.Add(new RequireHttpsAttribute());   
            }
            ...
    }
    
    0 讨论(0)
  • 2020-11-28 19:01

    This worked for me, MVC 6 (ASP.NET Core 1.0). Code checks if debug is in development, and if not, ssl is not required. All edits are in Startup.cs.

    Add:

    private IHostingEnvironment CurrentEnvironment { get; set; }
    

    Add:

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

    Edit:

    public void ConfigureServices(IServiceCollection services)
    {
        // additional services...
    
        services.AddMvc(options =>
        {
            if (!CurrentEnvironment.IsDevelopment())
            {
                options.Filters.Add(typeof(RequireHttpsAttribute));
            }
        });
    }
    
    0 讨论(0)
  • 2020-11-28 19:01

    After researching aroud, I was able to solve this issue with IIS Express and an override of the Controller class's OnAuthorization method (Ref#1). I have also gone with the route recommended by Hanselman (Ref#2). However, I was not complete satisfied with these two solutions due to two reasons: 1. Ref#1's OnAuthorization only works at the action level, not at the controller class level 2. Ref#2 requires a lot of setup (Win7 SDK for makecert), netsh commands, and, in order to use port 80 and port 443, I need to launch VS2010 as administrator, which I frown upon.

    So, I came up with this solution that focuses on simplicity with the following conditions:

    1. I want to be able to use the RequireHttps attbbute at Controller class or action level

    2. I want MVC to use HTTPS when the RequireHttps attribute is present, and use HTTP if it is absent

    3. I do not want to have to run Visual Studio as administrator

    4. I want to be able to use any HTTP and HTTPS ports that are assigned by IIS Express (See Note#1)

    5. I can reuse the self-signed SSL cert of IIS Express, and I do not care if I see the invalid SSL prompt

    6. I want dev, test, and production to have the exact same code base and same binary and as independent from additional setup (e.g. using netsh, mmc cert snap-in, etc.) as possible

    Now, with the background and explanation out of the way, I hope this code will help someone and save some time. Basically, create a BaseController class that inherits from Controller, and derive your controller classes from this base class. Since you have read this far, I assume that you know how to do these. So, happy coding!

    Note#1: This is achieved by the use of a useful function 'getConfig' (see code)

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

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

    ========== Code in 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);
        }
    

    ============== end code ================

    In Web.Release.Config, add the following to clear out HttpPort and HttpsPort (to use the default 80 and 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>
    
    0 讨论(0)
  • 2020-11-28 19:01

    MVC 6 (ASP.NET Core 1.0):

    The proper solution would be to use env.IsProduction() or env.IsDevelopment(). Read more about reason behind in this answer on how to require https only in production.

    Condensed answer below (see link above to read more about design decisions) for 2 different styles:

    1. Startup.cs - register filter
    2. BaseController - attribute style

    Startup.cs (register filter):

    public void ConfigureServices(IServiceCollection services)
    {
        // TODO: Register other services
    
        services.AddMvc(options =>
        {
            options.Filters.Add(typeof(RequireHttpsInProductionAttribute));
        });
    }
    

    BaseController.cs (attribute style):

    [RequireHttpsInProductionAttribute]
    public class BaseController : Controller
    {
        // Maybe you have other shared controller logic..
    }
    
    public class HomeController : BaseController
    {
        // Add endpoints (GET / POST) for Home controller
    }
    

    RequireHttpsInProductionAttribute: Both of above are using custom attribute inheriting from 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);
        }
    }
    
    0 讨论(0)
  • 2020-11-28 19:04

    How about inheriting the RequireHttps attribute in a custom attribute. Then, inside your custom attribute, check the IsLocal property of the current request to see if the request is coming from the local machine. If it is, then do not apply the base functionality. Otherwise, call the base operation.

    0 讨论(0)
  • 2020-11-28 19:06

    Please refer to this post by Rick Anderson on RickAndMSFT on Azure & MVC Filling the Azure Gap

    http://blogs.msdn.com/b/rickandy/archive/2011/04/22/better-faster-easier-ssl-testing-for-asp-net-mvc-amp-webforms.aspx

    0 讨论(0)
提交回复
热议问题