68

I have read the previous posts about using the RequireHttpsAttribute to secure individual controllers:

ASP.NET MVC RequireHttps in Production Only

but is there a way to apply this to the entire site? Due to my host (discountasp.net) I cannot use the "RequireSSL IIS" setting.

Community
  • 1
  • 1
CodeGrue
  • 5,865
  • 6
  • 44
  • 62
  • I would recommend a custom redirect module since it'll catch and redirect sooner in the mvc life cycle. I would do it differently than this article [ http://alexwolfthoughts.com/efficient-mvc-redirects-using-an-http-module ] but it shows some of the possibilities. – christo8989 Mar 29 '16 at 22:26

9 Answers9

132

Register the RequireHttpsAttribute as a global filter.

In global.asax:

protected void Application_Start()
{
    GlobalFilters.Filters.Add(new RequireHttpsAttribute());

    //... other stuff
}
mason
  • 31,774
  • 10
  • 77
  • 121
hwiechers
  • 14,583
  • 8
  • 53
  • 62
  • Definitely the cleanest method. – Serj Sagan Jan 20 '14 at 17:54
  • I'm doing this and I'm getting a redirection loop. The URL Rewrite worked for me. – Raciel R. Feb 21 '14 at 04:52
  • 4
    @RacielR. I had the same problem before, the problem was the hosting partner which accepted the ssl at the firewall but than send the request via http to my server... Make sure your hostingpartner isn't doing the same. – Julian Mar 05 '14 at 09:41
  • 2
    It's a clean solution, but wouldn't it only apply to MVC controllers? What if you wanted to enforce HTTPS for _any_ URL, including static resources or legacy Web Forms aspx pages? It seems the URL Rewrite method is the most universal. Would even work for Node.js applications hosted in iisnode (I know because I'm doing it). – dprothero May 21 '15 at 14:45
  • I applied this to my application while using IIS Express and all it does it redirect to "https://localhost". – Joel McBeth Sep 22 '15 at 23:32
  • I would rather use the "RegisterGlobalFilters" method in Global.asax to register global filters (like RequireHttps,Authorize etc.). I added an example in my answer below. – Dhanuka777 Apr 18 '16 at 05:03
  • 2
    RequireHttpsAttribute sends 302(temp redir) instead of 301 (permanent), which can hurt SEO. http://benjii.me/2015/10/redirect-to-https-asp-net-mvc/ – Sam Rueby Apr 19 '16 at 19:24
  • FYI if anyone uses OverrideAuthorizationAttribute on a controller or action it clears out the global filter. – odyth Jul 11 '17 at 21:38
  • @Sam.Rueby GlobalFilters.Filters.Add(new RequireHttpsAttribute(true)); // pass true for permanent – Kris Feb 26 '18 at 04:47
24

I ended up using IIS URL Rewrite 2.0 to force the site to switch to HTTPS. This code in web.config does the trick:

  <system.webServer>
    <!-- This uses URL Rewrite 2.0 to force the entire site into SSL mode -->
    <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://{HTTP_HOST}/{R:1}" appendQueryString="true" redirectType="Permanent" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
CodeGrue
  • 5,865
  • 6
  • 44
  • 62
  • 2
    For anyone finding this years later like me, this threw an error for me in vs2017. The following answer similar to this worked: https://stackoverflow.com/a/47095/4180481 – Ozan Nov 03 '17 at 12:48
20

You could always add a check at the application level in your global.asax

protected void Application_BeginRequest(Object sender, EventArgs e)
{
   if (!HttpContext.Current.Request.IsSecureConnection)
   {
    Response.Redirect("https://" + Request.ServerVariables["HTTP_HOST"]
                                 + HttpContext.Current.Request.RawUrl);
   }
}
Bellash
  • 7,560
  • 6
  • 53
  • 86
Todd Smith
  • 17,084
  • 11
  • 59
  • 78
17

Just to bring this answer upto date for MVC 3 and above use the following in your Filterconfig.cs file within the App_start folder

        filters.Add(new RequireHttpsAttribute());

Obviously you will need your servers IIS configured to use a valid SSL certificate, cheap certs can be purchased here: https://www.namecheap.com/ i think the last time i purchased one it was $9 per domain per year.

Alex Stephens
  • 3,017
  • 1
  • 36
  • 41
  • Appreciate you taking the time to come back and update this question with an up-to-date answer – James Wilson May 15 '15 at 20:46
  • is RequireHttpsAttribute expected to redirect users to https when users type http, or will it throw a 403 error instead? – usefulBee Jan 14 '16 at 17:44
  • Interestingly, the RequireHttpsAttribute sends a 302 (temporary) redirect NOT a 301. Strange because a 301 is a permanent redirect which seems farrr more applicable here. Anyway it's important to remember it's only the first call to the site over HTTP that pushes the 302 to HTTPS and then all subsequent calls within that session are within the HTTPS protocol. There are way to overcome the 302, 1 way is suggested here by Ben http://benjii.me/2015/10/redirect-to-https-asp-net-mvc/ – Alex Stephens Jan 15 '16 at 05:55
14

In your FilterConfig.cs apply this:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
     // only if Debug is not enabled, do not require https for local development
     if (!HttpContext.Current.IsDebuggingEnabled)
          filters.Add(new RequireHttpsAttribute());

     //... any other filters
}

That should force your app to use https on every page.

joegreentea
  • 325
  • 3
  • 12
4

This isn't using RequireHttps but I think it's a better solution because it catches the redirect sooner in the MVC Lifecycle.

public class RedirectModule : IHttpModule
{
    private HttpApplication _context;

    public void Init(HttpApplication context)
    {
        _context = context;
        _context.PostResolveRequestCache += HttpRedirect;
    }

    public void HttpRedirect(Object src, EventArgs args)
    {
        if (_context.Request.Url.Scheme == Uri.UriSchemeHttp)
        {
            //Redirect to https
            var scheme = Uri.UriSchemeHttps + "://";
            var authority = _context.Request.Url.Authority;
            var url = _context.Request.RawUrl;

            var redirectTo = scheme + authority + url;
            _context.Response.PermanentRedirect(redirectTo);
        }
    }

    public void Dispose() { }
}

The idea came from this article.

You can register the module in your Web.config or inside the Global.asax. I'll show you in the web.cofig.

<system.webServer>
    <modules>
        <add name="ConfigModuleName" type="Your.Namespace.RedirectModule"/>
    </modules>
</system.webServer>
christo8989
  • 6,442
  • 5
  • 37
  • 43
  • Application_BeginRequest seems to get called before the event handler registered at PostResolveRequestCache – odyth Jul 11 '17 at 22:08
4

MVC 6 (ASP.NET Core 1.0) works slightly different in it's way of registering filters:

Startup.cs - AddMvc with filter for RequireHttpsAttribute:

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

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

Design decisions explained:

  1. Use filter in Startup.cs for global setup (since we want this to apply everywhere). Startup should be responsible for registering and setting up all global rules. If your company employ a new developer, she would expect to find global setup in Startup.cs.
  2. Use RequireHttpsAttribute logic since it's proven (by Microsoft). Never use "magical" strings like "http://" and "https://" when it can be avoided by reusing a Microsoft component created to provide the same logic.

If you are running your MVC website in localhost without SSL:

  • http://localhost:1337/ (no SSL)
  • https://localhost:1337/ (SSL)

Consider looking at how to run without SSL in localhost while still requiring https it in production.

Note:

As an alternative, we could make a "class BaseController : Controller" and make all our controllers inherit from "BaseController" (instead of Controller). Then we only have to set the attribute 1 global place (and don't need to register filter in Startup.cs).

Some people prefer the attribute style.

Example of usage:

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

public class HomeController : BaseController
{
    // Add endpoints (GET / POST) for Home controller
}
Community
  • 1
  • 1
Nick Niebling
  • 317
  • 3
  • 12
0

In Global.asax.cs, use "RegisterGlobalFilters" to register global attributes.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new RequireHttpsAttribute());
    //e.g. filters.Add(new HandleErrorAttribute());
    //e.g. filters.Add(new System.Web.Mvc.AuthorizeAttribute());            
}
Dhanuka777
  • 8,331
  • 7
  • 70
  • 126
-2

You could use a base class for all of your controllers, and decorate that with the require ssl attribute.

DanP
  • 6,310
  • 4
  • 40
  • 68
  • 4
    I (or another team member) don't want to accidentally forget to add the attribute to a new controller, or likewise, forget to inherit from the base class. We want it to be foolproof. – CodeGrue Jul 19 '10 at 21:02
  • @CodeGrue: you could always add a unit test that asserts that all of your controllers use this attribute; see my recent question regarding the serializable attribute: http://stackoverflow.com/questions/3257004/how-to-determine-if-all-objects-are-serializable-in-a-given-namespace – DanP Jul 19 '10 at 21:05