Best way in asp.net to force https for an entire site?
Asked Answered
T

16

212

About 6 months ago I rolled out a site where every request needed to be over https. The only way at the time I could find to ensure that every request to a page was over https was to check it in the page load event. If the request was not over http I would response.redirect("https://example.com")

Is there a better way -- ideally some setting in the web.config?

Thousandfold answered 5/9, 2008 at 23:34 Comment(3)
check my answer here #33882850Limburger
Related post - How to force HTTPS using a web.config fileDb
Check this post: codingfusion.com/Post/…Venn
C
275

Please use HSTS (HTTP Strict Transport Security)

from http://www.hanselman.com/blog/HowToEnableHTTPStrictTransportSecurityHSTSInIIS7.aspx

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="HTTP to HTTPS redirect" stopProcessing="true">
                    <match url="(.*)" />
                    <conditions>
                        <add input="{HTTPS}" pattern="off" ignoreCase="true" />
                    </conditions>
                    <action type="Redirect" url="https://{HTTP_HOST}/{R:1}"
                        redirectType="Permanent" />
                </rule>
            </rules>
            <outboundRules>
                <rule name="Add Strict-Transport-Security when HTTPS" enabled="true">
                    <match serverVariable="RESPONSE_Strict_Transport_Security"
                        pattern=".*" />
                    <conditions>
                        <add input="{HTTPS}" pattern="on" ignoreCase="true" />
                    </conditions>
                    <action type="Rewrite" value="max-age=31536000" />
                </rule>
            </outboundRules>
        </rewrite>
    </system.webServer>
</configuration>

Original Answer (replaced with the above on 4 December 2015)

basically

protected void Application_BeginRequest(Object sender, EventArgs e)
{
   if (HttpContext.Current.Request.IsSecureConnection.Equals(false) && HttpContext.Current.Request.IsLocal.Equals(false))
   {
    Response.Redirect("https://" + Request.ServerVariables["HTTP_HOST"]
+   HttpContext.Current.Request.RawUrl);
   }
}

that would go in the global.asax.cs (or global.asax.vb)

i dont know of a way to specify it in the web.config

Callum answered 5/9, 2008 at 23:42 Comment(17)
This works, but it was dangerous for me: when I attempted to run locally in VS 2010 with this code running, my start page never loaded; instead, I just received a "This webpage is not available" message. To fix, I added a second condition to test if the url contains the string "localhost": if it does not, then force https.Z
This is giving me a redirect loop. Before I added the code it worked fine. Any suggestions?Carpal
+1, It is doing the redirect to server twice? First one, When you are explicitly redirecting and second one, when you are doing redirect in the Global.asax ?Signac
Please note that this does not provide any useful security whatsoever. Actually, it will only secure connections from users that are already safe, and will fail to secure those that are being attacked (this is because a MITM can simply omit the redirection altogether and forward everything to your "secure" site). IMHO, redirecting user agents is just feel-good voodoo security, and provides a sometimes dangerous illusion of safety. The only way to go is to instruct the user agents to only request secure resources, not redirect them if they don't. This is what HSTS does -- see answers below.Ashantiashbaugh
You can also add - "GlobalFilters.Filters.Add(new RequireHttpsAttribute());" to "Application_Start()" method in "global.asax.cs"Lunna
This answer should be considered 'harmful' and should not be used. As per comment by @Ashantiashbaugh above.Subbase
If you're getting a reported error about your web.config being malformed make sure you have the url rewrite module installed - that had me stuck for 10 minutes or so. iis.net/downloads/microsoft/url-rewriteRadioman
@RosdiKasim Should this answer still be considered harmful since the Dec 4 '15 edit?Swinson
@AndrewMorton The updated answer (using HSTS) is the proper way to do it. It also redirects the browser to https in case the browser is using http.Subbase
This doesn't work for me. The site never get redirected to. I edited my web.config as above. The site works fine without the web.config edit and the https site works just fine if I navigate straight there (assuming no web.config changes). Has anyone else had issues with this code?Cittern
I get a different problem. No redirect, but I can't get to the https site whether I have the web.config updated or not. firefox says SSL_ERROR_RX_RECORD_TOO_LONG and IE says to turn on TLS 1, 1.1, and 1.2 and try again.Lewin
I've always had to use <add input="{HTTPS}" pattern="on" negate="true" /> because off never worked. Maybe it does now, I don't even try it these days.Fulcrum
I copied these line to my web.config and now https is not working. I deleted these lines from web.config but my site still doesn't work. It seems to me, that these few lines modified something in my system that is connected to this project.Thanks
After hsts is enabled for a domain the browser will not allow a non secure connection. You either need to make https works or clear the hsts for the domain on your browser.Callum
@Ashantiashbaugh don't you first have to write code to redirect a user from http to https, in order to be able to respond with the HSTS? HSTS is only read by the browser if it's using HTTPS.Discriminator
@DavidKlempfner In an ideal world this is not the best solution. The best would be to encourage end-users to only use HTTPS (it's now built-in to browsers, see "HTTPS-Only Mode" in Firefox for example, but there has always been extensions). HSTS helps if one needs to build lists of sites that can be secured (like HTTPS Everywhere), by declaring "this whole domain supports HTTPS, do use it. An insecure redirection + HSTS can be seen as a form of TOFU and as such is definitely better than plain HTTP, but will not be better than closing port 80.Ashantiashbaugh
When I paste the revised answer's <rewrite> ... </rewrite> block into the <system.webServer> section of my web.config file, there is a 500 error.Burson
W
130

The other thing you can do is use HSTS by returning the "Strict-Transport-Security" header to the browser. The browser has to support this (and at present, it's primarily Chrome and Firefox that do), but it means that once set, the browser won't make requests to the site over HTTP and will instead translate them to HTTPS requests before issuing them. Try this in combination with a redirect from HTTP:

protected void Application_BeginRequest(Object sender, EventArgs e)
{
  switch (Request.Url.Scheme)
  {
    case "https":
      Response.AddHeader("Strict-Transport-Security", "max-age=300");
      break;
    case "http":
      var path = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
      Response.Status = "301 Moved Permanently";
      Response.AddHeader("Location", path);
      break;
  }
}

Browsers that aren't HSTS aware will just ignore the header but will still get caught by the switch statement and sent over to HTTPS.

Whitehead answered 22/11, 2011 at 22:6 Comment(10)
Never heard about the HSTS header before, but looks pretty cool. Is there any reason for using such a small max-age value (5 minutes)? The Wikipedia article you link to suggests setting it to a large value (6-12 months).Gluck
+1. check out this very extensive article on Troy's blog which includes details on why only using redirects can reduce security. Hint: it can leave you vulnerable to the SSL Strip tool, among other things. troyhunt.com/2011/11/…Heimer
Also worth checking out NWebsec, which makes this (and more) very easy.Siesta
You'll want to wrap the switch in if(!Request.IsLocal) so it doesn't break debugging.Crooks
Good answer. One subtlety - For the Http headers ("Strict-Transport-Security") it is better to use library like NWebSec because there are multiple options which are concentrated in one place of configuration rather then spread out here and there.Achievement
@RosdiKasim, the accepted answer was updated for HSTS as well, but completely obviated the "old answer" that showed the redirect-only in code. This is a good one for doing it in code. Can also be written into an IHttpModule.Seely
A quick conversion to VB.net and this solution worked great.Nefen
Due to a couple of non-tech issues, this is the method I used... except I'm returning "308 Permanent Redirect" which ensures that POSTs stay POSTs for the http redirect.Trioecious
I first used montys suggestion to use a 308 instead of 301 because of the POST support, only to find out that a 308 is not supported by IE11. If a significant portion of your visitors is still on IE, this could be a concern.Affranchise
Does this handle all traffic to that website? or just the pages you've coded? That is, what about my images and javascript files?Lewin
W
92

The IIS7 module will let you redirect.

    <rewrite>
        <rules>
            <rule name="Redirect HTTP to HTTPS" stopProcessing="true">
                <match url="(.*)"/>
                <conditions>
                    <add input="{HTTPS}" pattern="^OFF$"/>
                </conditions>
                <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="SeeOther"/>
            </rule>
        </rules>
    </rewrite>
Wendel answered 15/2, 2010 at 22:46 Comment(2)
Also, for IIS 7.0, you need to install Url Rewrite Module 2.0Yorick
I found this link simple and helpful in making any particular page to accept only https requests - support.microsoft.com/kb/239875Lunna
P
25

For those using ASP.NET MVC. You can use the following to force SSL/TLS over HTTPS over the whole site in two ways:

The Hard Way

1 - Add the RequireHttpsAttribute to the global filters:

GlobalFilters.Filters.Add(new RequireHttpsAttribute());

2 - Force Anti-Forgery tokens to use SSL/TLS:

AntiForgeryConfig.RequireSsl = true;

3 - Require Cookies to require HTTPS by default by changing the Web.config file:

<system.web>
    <httpCookies httpOnlyCookies="true" requireSSL="true" />
</system.web>

4 - Use the NWebSec.Owin NuGet package and add the following line of code to enable Strict Transport Security accross the site. Don't forget to add the Preload directive below and submit your site to the HSTS Preload site. More information here and here. Note that if you are not using OWIN, there is a Web.config method you can read up on on the NWebSec site.

// app is your OWIN IAppBuilder app in Startup.cs
app.UseHsts(options => options.MaxAge(days: 30).Preload());

5 - Use the NWebSec.Owin NuGet package and add the following line of code to enable Public Key Pinning (HPKP) across the site. More information here and here.

// app is your OWIN IAppBuilder app in Startup.cs
app.UseHpkp(options => options
    .Sha256Pins(
        "Base64 encoded SHA-256 hash of your first certificate e.g. cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=",
        "Base64 encoded SHA-256 hash of your second backup certificate e.g. M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=")
    .MaxAge(days: 30));

6 - Include the https scheme in any URL's used. Content Security Policy (CSP) HTTP header and Subresource Integrity (SRI) do not play nice when you imit the scheme in some browsers. It is better to be explicit about HTTPS. e.g.

<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.4/bootstrap.min.js"></script>

The Easy Way

Use the ASP.NET MVC Boilerplate Visual Studio project template to generate a project with all of this and much more built in. You can also view the code on GitHub.

Prau answered 18/3, 2015 at 15:29 Comment(5)
Also if using <authentication mode="Forms">, inside you must have <forms requireSSL="true">Argentine
@muhammad-rehan-saeed I am using mvc5 boilerplate but the site does not redirect http to https automatically on production server it does so only on localhost is there something I am missing?Arse
This is not the right forum to ask this question. Post an issue on the GitHub site. The RequireHttpsAttribute does the redirect. As long as you have that it should be fine.Prau
@MuhammadRehanSaeed, love your answer. But... how do I get the SHA256 hash of a certificate created with MakeCert? All I have is a SHA-1 thumbprint... Do you happen to know?Emileemilee
@Emileemilee this link can show you how.Prau
F
13

If you are unable to set this up in IIS for whatever reason, I'd make an HTTP module that does the redirect for you:

using System;
using System.Web;

namespace HttpsOnly
{
    /// <summary>
    /// Redirects the Request to HTTPS if it comes in on an insecure channel.
    /// </summary>
    public class HttpsOnlyModule : IHttpModule
    {
        public void Init(HttpApplication app)
        {
            // Note we cannot trust IsSecureConnection when 
            // in a webfarm, because usually only the load balancer 
            // will come in on a secure port the request will be then 
            // internally redirected to local machine on a specified port.

            // Move this to a config file, if your behind a farm, 
            // set this to the local port used internally.
            int specialPort = 443;

            if (!app.Context.Request.IsSecureConnection 
               || app.Context.Request.Url.Port != specialPort)
            {
               app.Context.Response.Redirect("https://" 
                  + app.Context.Request.ServerVariables["HTTP_HOST"] 
                  + app.Context.Request.RawUrl);    
            }
        }

        public void Dispose()
        {
            // Needed for IHttpModule
        }
    }
}

Then just compile it to a DLL, add it as a reference to your project and place this in web.config:

 <httpModules>
      <add name="HttpsOnlyModule" type="HttpsOnly.HttpsOnlyModule, HttpsOnly" />
 </httpModules>
Freedafreedman answered 6/9, 2008 at 0:3 Comment(5)
This seems more involved than just sticking it in the global.asax -- just curious, is there an advantage?Telemeter
The advantage would be, when you don't want to use it, just comment out the module in your web.config. This solution is configurable, whereas the other is not.Palpable
I am little confused. I'd expected something like app.BeginRequest += new OnBeginRequest; in the Init method and in the OnBeginRequest would contains what current Init method contains. Are you sure that this module works as expected?Harmonics
It does not work. You do need to add the OnBeginRequest event etc, then it works.Tamberg
I would edit this faulty code, but to make it secure you'd also need to use HSTS. Just go with Troy Hunt's answer and make it a module; see support.microsoft.com/en-us/kb/307996 (an oldie, but goodie).Seely
O
5

What you need to do is :

1) Add a key inside of web.config, depending upon the production or stage server like below

<add key="HttpsServer" value="stage"/>
             or
<add key="HttpsServer" value="prod"/>

2) Inside your Global.asax file add below method.

void Application_BeginRequest(Object sender, EventArgs e)
{
    //if (ConfigurationManager.AppSettings["HttpsServer"].ToString() == "prod")
    if (ConfigurationManager.AppSettings["HttpsServer"].ToString() == "stage")
    {
        if (!HttpContext.Current.Request.IsSecureConnection)
        {
            if (!Request.Url.GetLeftPart(UriPartial.Authority).Contains("www"))
            {
                HttpContext.Current.Response.Redirect(
                    Request.Url.GetLeftPart(UriPartial.Authority).Replace("http://", "https://www."), true);
            }
            else
            {
                HttpContext.Current.Response.Redirect(
                    Request.Url.GetLeftPart(UriPartial.Authority).Replace("http://", "https://"), true);
            }
        }
    }
}
Olive answered 7/10, 2014 at 9:6 Comment(0)
D
5

In IIS10 (Windows 10 and Server 2016), from version 1709 onwards, there is a new, simpler option for enabling HSTS for a website.

Microsoft describe the advantages of the new approach here, and provide many different examples of how to implement the change programmatically or by directly editing the ApplicationHost.config file (which is like web.config but operates at the IIS level, rather than individual site level). ApplicationHost.config can be found in C:\Windows\System32\inetsrv\config.

I've outlined two of the example methods here to avoid link rot.

Method 1 - Edit the ApplicationHost.config file directly Between the <site> tags, add this line:

<hsts enabled="true" max-age="31536000" includeSubDomains="true" redirectHttpToHttps="true" />

Method 2 - Command Line: Execute the following from an elevated command prompt (i.e. right mouse on CMD and run as administrator). Remember to swap Contoso with the name of your site as it appears in IIS Manager.

c:
cd C:\WINDOWS\system32\inetsrv\
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.enabled:True" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.max-age:31536000" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.includeSubDomains:True" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.redirectHttpToHttps:True" /commit:apphost

The other methods Microsoft offer in that articles might be better options if you are on a hosted environment where you have limited access.

Keep in mind that IIS10 version 1709 is available on Windows 10 now, but for Windows Server 2016 it is on a different release track, and won't be released as a patch or service pack. See here for details about 1709.

Dicast answered 24/11, 2018 at 11:43 Comment(0)
J
3

If SSL support is not configurable in your site (ie. should be able to turn https on/off) - you can use the [RequireHttps] attribute on any controller / controller action you wish to secure.

Jolley answered 1/6, 2014 at 8:20 Comment(0)
G
3

This is a fuller answer based on @Troy Hunt's. Add this function to your WebApplication class in Global.asax.cs:

    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
        // Allow https pages in debugging
        if (Request.IsLocal)
        {
            if (Request.Url.Scheme == "http")
            {
                int localSslPort = 44362; // Your local IIS port for HTTPS

                var path = "https://" + Request.Url.Host + ":" + localSslPort + Request.Url.PathAndQuery;

                Response.Status = "301 Moved Permanently";
                Response.AddHeader("Location", path);
            }
        }
        else
        {
            switch (Request.Url.Scheme)
            {
                case "https":
                    Response.AddHeader("Strict-Transport-Security", "max-age=31536000");
                    break;
                case "http":
                    var path = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
                    Response.Status = "301 Moved Permanently";
                    Response.AddHeader("Location", path);
                    break;
            }
        }
    }

(To enable SSL on your local build enable it in the Properties dock for the project)

Geulincx answered 10/9, 2016 at 11:57 Comment(0)
B
2

It also depends on the brand of your balancer, for the web mux, you would need to look for http header X-WebMux-SSL-termination: true to figure that incoming traffic was ssl. details here: http://www.cainetworks.com/support/redirect2ssl.html

Bluegill answered 16/11, 2010 at 2:31 Comment(0)
K
2

For @Joe above, "This is giving me a redirect loop. Before I added the code it worked fine. Any suggestions? – Joe Nov 8 '11 at 4:13"

This was happening to me as well and what I believe was happening is that there was a load balancer terminating the SSL request in front of the Web server. So, my Web site was always thinking the request was "http", even if the original browser requested it to be "https".

I admit this is a bit hacky, but what worked for me was to implement a "JustRedirected" property that I could leverage to figure out the person was already redirected once. So, I test for specific conditions that warrant the redirect and, if they are met, I set this property (value stored in session) prior to the redirection. Even if the http/https conditions for redirection are met the second time, I bypass the redirection logic and reset the "JustRedirected" session value to false. You'll need your own conditional test logic, but here's a simple implementation of the property:

    public bool JustRedirected
    {
        get
        {
            if (Session[RosadaConst.JUSTREDIRECTED] == null)
                return false;

            return (bool)Session[RosadaConst.JUSTREDIRECTED];
        }
        set
        {
            Session[RosadaConst.JUSTREDIRECTED] = value;
        }
    }
Kneepad answered 12/12, 2012 at 19:46 Comment(0)
M
2

I'm going to throw my two cents in. IF you have access to IIS server side, then you can force HTTPS by use of the protocol bindings. For example, you have a website called Blah. In IIS you'd setup two sites: Blah, and Blah (Redirect). For Blah only configure the HTTPS binding (and FTP if you need to, make sure to force it over a secure connection as well). For Blah (Redirect) only configure the HTTP binding. Lastly, in the HTTP Redirect section for Blah (Redirect) make sure to set a 301 redirect to https://blah.com, with exact destination enabled. Make sure that each site in IIS is pointing to it's own root folder otherwise the Web.config will get all screwed up. Also make sure to have HSTS configured on your HTTPSed site so that subsequent requests by the browser are always forced to HTTPS and no redirects occur.

Magnetometer answered 12/11, 2015 at 21:56 Comment(0)
B
2

I spent sometime looking for best practice that make sense and found the following which worked perfected for me. I hope this will save you sometime.

Using Config file (for example an asp.net website) https://blogs.msdn.microsoft.com/kaushal/2013/05/22/http-to-https-redirects-on-iis-7-x-and-higher/

or on your own server https://www.sslshopper.com/iis7-redirect-http-to-https.html

[SHORT ANSWER] Simply The code below goes inside

<system.webServer> 
 <rewrite>
     <rules>
       <rule name="HTTP/S to HTTPS Redirect" enabled="true" 
           stopProcessing="true">
       <match url="(.*)" />
        <conditions logicalGrouping="MatchAny">
        <add input="{SERVER_PORT_SECURE}" pattern="^0$" />
       </conditions>
       <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" 
        redirectType="Permanent" />
        </rule>
       </rules>
 </rewrite>
Bethlehem answered 7/9, 2018 at 21:29 Comment(0)
F
1

-> Simply ADD [RequireHttps] on top of the public class HomeController : Controller.

-> And add GlobalFilters.Filters.Add(new RequireHttpsAttribute()); in 'protected void Application_Start()' method in Global.asax.cs file.

Which forces your entire application to HTTPS.

Fantasia answered 13/3, 2016 at 17:12 Comment(1)
I don't believe this will work for any pages served using WebForms or any APIs built with WebAPI. It will only cover MVC controllers.Seely
S
1

If you are using ASP.NET Core you could try out the nuget package SaidOut.AspNetCore.HttpsWithStrictTransportSecurity.

Then you only need to add

app.UseHttpsWithHsts(HttpsMode.AllowedRedirectForGet, configureRoutes: routeAction);

This will also add HTTP StrictTransportSecurity header to all request made using https scheme.

Example code and documentation https://github.com/saidout/saidout-aspnetcore-httpswithstricttransportsecurity#example-code

Swafford answered 23/3, 2017 at 6:56 Comment(0)
G
0

Here we discover, the version off Chromium Engine fixed soms DNS to force HTTP and it does it (Force HTTPS) regardless of the value.

Check is your DNS exists in this list (our DNS exists, but why?):

https://chromium.googlesource.com/chromium/src/+/653108a127a957fc8a103b738f58a08aaf163a2f%5E%21/

We nos have solution yet

Gabardine answered 22/9, 2023 at 12:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.