Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

A dynamic RequireSsl Attribute for ASP.NET MVC


sslIn most of my Web applications I’m finding that I need to handle SSL activation dynamically rather than statically. IOW, depending on the environment that I’m running in I need to specify whether I want to enforce SSL or not. Specifically during development I typically want to run with SSL disabled and at runtime on my live server I want to force it on. On a staging server typically I don’t want to run SSL unless I have access to a configured certificate.

Typically there’s little reason to run SSL locally on development machines, and it certainly isn’t configured by default. Although IIS makes it pretty easy to create machine certificates these days, it’s still not quite automatic for SSL to ‘just work’ out of the box. I find especially in multi-developer environments or for staging and testing servers adding certificates is often a causing a problem or adding extra work that doesn’t really add any value on a non-production machine.

For these reasons I like to use a configuration switch to turn SSL on and off at runtime based on a configuration setting.

SSL and MVC

ASP.NET MVC provides the [RequireHttps] attribute that you can slap on a controller or controller method and which forces the affected requests to SSL. This works fine for static situations if you want to force a controller or method to SSL, but it’s on or off and you have to change code in order to change the value.

It’s easy enough to use: You simply apply the attribute to a control or individual controller methods and off you go.

Here’s what this looks like on a controller:

[RequireHttps]
public class AccountController : AppBaseController

You can also assign [RequireHttps] to a method – if you don’t have it on the controller – to force individual methods to be accessed via SSL.

[RequireHttps]
public ActionResult Login(string redirectUrl)  {…}

For example, this can be useful if you have a login API and you only want to protect your login pages or certain order pages in an online store etc. Personally, if I have a certificate on my site – these days I prefer to simply run the entire site in SSL since it’s more secure and also cleaner simply leaving all urls consistently in SSL rather than switching back and forth which can result in different urls that sometimes show up in SSL and sometimes now (because once you go to URL and then navigate to a non-SSL required URL you stay in SSL).

The implementation of this attribute is super simple – you can check out the code here on GitHub. Basically all this method does is check to see if the request is on a secure connection and if it isn’t, switch the URL to https by munging request URI and then redirecting to the same page with the new URL. It’s important to note that the attribute only works with GET requests, which makes sense, since a redirect cannot pass any POST content to the redirected page.

Working around [RequireHttps]

Because [RequireHttps] is an attribute it requires constant values as parameters, and there’s no option to dynamically determine whether the controller should run SSL or not. So it’s really all or nothing and a manual code change to flip behavior, which is not super useful if you want to parameterize SSL operation with a configuration switch or some other mechanism.

To get around the static setting in RequireHttps in the past I have simply created a custom attribute that overrides the RequireHttpsAttribute behavior, by subclassing it and creating my own attribute. I then handle the default constructor’s code and load a setting from within the application from a configuration store – in this case a simple configuration setting on a config class.

Here’s what this custom attribute looks like:

public class RequireHttpsWithFlagAttribute : RequireHttpsAttribute
{
    public bool RequireSsl { get; set; }
    
    public RequireHttsWithFlagAttribute()
    {
        // Assign from App specific configuration object
        RequireSsl = App.Configuration.RequireSsl;
    }

    public RequireHttpsWithFlagAttribute(bool requireSsl)
    {
        RequireSsl = requireSsl;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext != null &&
            RequireSsl &&
            !filterContext.HttpContext.Request.IsSecureConnection)
        {
            HandleNonHttpsRequest(filterContext);
        }
    }
}

The key line in this subclassed version is the default constructor which overrides the RequireSsl flag with a configuration value which comes from my web.config file. Here I’m using the West Wind Application Configuration class to hold and retrieve my value, but the value could really come from anywhere as long as it’s globally available. In this case the App.Configuration is a static class so I can globally access it here in the attribute. You could use an AppSettings key or whatever else works for you for configuration.

This code also overrides the OnAuthorization call, duplicating the original functionality but adding in the RequireSsl flag setting as well into the filter condition.

Once this attribute has been created I can access it on a controller class or Action method just as I normally would do with [RequireHttps]:

[RequireHttpsWithFlag]
public class AccountController : AppBaseController    

Because I use the default constructor, the attribute assigns the value from the configuration and that’s it – I’m in business with my dynamic value. You can also explicitly pass true or false to this attribute to enable and disable SSL behavior, which is nice for explicitly setting and removing SSL from requests.

Posted in ASP.NET  MVC  Security  

The Voices of Reason


 

Joshua Kincaid
June 18, 2014

# re: A dynamic RequireSsl Attribute for ASP.NET MVC

What is stopping you from using a IOC Container in your attribute constructors and resolve a interface to a concrete implementation that knows how to access you configuration settings. In this way you could store the settings anywhere.

Diego Mijelshon
June 18, 2014

# re: A dynamic RequireSsl Attribute for ASP.NET MVC

Interesting approach. For a simpler site-wide configurable forcing of https we are using this transform:
  <system.webServer>
    <rewrite xdt:Transform="Insert">
      <rules>
        <rule name="Force HTTPS" enabled="true">
          <match url="(.*)" ignoreCase="false" />
          <conditions>
            <add input="{HTTPS}" pattern="off" />
          </conditions>
          <action type="Redirect" url="https://{SERVER_NAME}/{R:1}" appendQueryString="true" redirectType="Found" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>

Steven
June 18, 2014

# re: A dynamic RequireSsl Attribute for ASP.NET MVC

Another option to allow more dynamic registration would be to inject the attribute with a DI framework. Containers like Ninject support lamba expressions for specifying when certain types are injected versus not injected.

kernel.BindHttpFilter<MyAttribute>(FilterScope.Controller).When(f => SomethingIsTrue());

Rick Strahl
June 18, 2014

# re: A dynamic RequireSsl Attribute for ASP.NET MVC

@Joshua, Steven - yes DI would do the trick as well, but I don't want to solely depend on IOC to have this work. Good point though - adding an injectable interface might be another good constructor to add.

@Steven - hmm, a selective injector might produce some funky behavior. You'd have to always inject in order to get consistent result me thinks.

@Diego - Excellent point about UrlRewrite. This works wonderfully if you do site wide SSL translation. I do like the idea though of having the control inside of the application to specify what gets pushed to ssl and what doesn't. Some of the things we do is have configuration that can be changed at runtime by an admin user - that'd be more difficult to do with Rewrite rules since they're not part of the application and you'd have to write out XML by hand. Definitely a good choice in some scenarios though - and it's also one of the most performant ones. Rewrite would also allow doing away with the POST limitation.

John
June 18, 2014

# re: A dynamic RequireSsl Attribute for ASP.NET MVC

My the same approcach (very similar implementation) is already used in nopCommerce

Rick Strahl
June 28, 2014

# re: A dynamic RequireSsl Attribute for ASP.NET MVC

@Steven - After the comments here I took a quick look to see what's involved in using IOC with a filter and it looks like doing IOC with a filter would be pretty tough to do *generically*.

Looking at some of the threads on StackOverflow it appears that constructor injection in general doesn't work - you need to use property injection at which point you start depending on specific IOC container dependencies to mark the property as injectable.

Has anybody done something along these lines? How would you create a generically injectable attribute that works with any IOC container for a filter?

Davide
November 14, 2014

# re: A dynamic RequireSsl Attribute for ASP.NET MVC

Hi Rick,

I used your solution modifying it a little bit to my project. I think you missed how to use a custom SSL port on development machine. After spending a few hours googling, I found out here a solution http://stackoverflow.com/questions/7104703/how-to-specify-a-different-port-with-the-requirehttps-attribute-in-mvc3. I applied a rewrite url in web.config.

Cheers,
Davide

Tim
March 22, 2015

# re: A dynamic RequireSsl Attribute for ASP.NET MVC

Hi Rick,

We use this little trick when decorating classes/methods:

#if !DEBUG
[RequireHttps]
#endif

public ActionResult MyAction()
{
...
}


For those not familiar, this uses the debug flag set up against the project configuration in Build tab. 'Define Debug constant'. You can configure whether the flag is set or not for each project build profile.
 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2017