RequireHttps and routing to https URL

9.5k views Asked by At

I am using the RequireHttps attribute on most of the actions in my User controller which handles the login and my secure https pages.

On my Home Page which is http I have a link to my login page as follows (MVC 4 Razor View):-

<a href="@Url.Action("Login", "User")">Login</a>

The link correctly goes to the login page with an https address. However, when I look in the IIS log I see there are two entries for the login URL, one on port 80 and one on port 443.

Is this an issue I should be concerned about?

I know on my @Url.Action I could force https mode, but not sure if this is the best way. Plus this removes the port, which is annoying when using IIS Express in VS 2012. I'd then have to further extend the @Url.Action to include the hostname:port.

So I am just checking if (a) this should be a concern and (b) if it is a concern whether there are any other ways to forcing the URL to https.

3

There are 3 answers

6
Nick Albrecht On BEST ANSWER

Most tutorials will agree that by having a mixed mode site (both HTTP and HTTPS) you're defeating the purpose of SSL (having certain paths require SSL then switching back to a non SSL connection). Once you switch to HTTPS it's recommended that you force the user to stay using HTTPS for everything, at the very least until they logout. I have one site that uses HTTPS and once you hit the site, I just use a URL Rewrite rule to switch the user to HTTPS, and only HTTPS is allowed.

<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>

Once you do this it's also recommended to set the authentication cookie to require HTTPS as well. For Forms authentication this is as simple as setting the following in your web.config.

<authentication>
    <forms requireSSL="true" />
</authentication>

I would also recommend looking at some of the Headers that can be set to help browsers treat the site correctly, like using Strict-Transport-Security to signify that the site requires SSL. Keep in mind that while Headers are a great supplemental measure you can take, ultimately they are left for the browser to enforce and should not be relied on exclusively.

I recommend these steps because I don't suffer from the symptom you're describing and am hoping they'll help resolve the issue you're noticing in the logs.

AMMENDMENT: Oh and I also forgot to mention, HTTPS is a little more intensive to establish a connection with than plain old HTTP. Not much in the grand scheme of things but still it's something. I'd recommend you utilize HTTP Keep Alive so as to reduce some of that overhead.

UPDATE: Communicating over SSL doesn't imply that you are authenticated. It just means you're talking over a secure/encrypted connection.

Take your Amazon example lets say. If you visit the site, you'll like get just a normal connection over HTTP. You're aren't logged in you're just browsing the site. If you wanted you could switch to HTTPS and you'd still get the same site but you're not logged in yet. Now if you try to login you'll get redirected so that you talk over SSL (if you're not already) as noted by the HTTPS moniker. Even after you actually login you will still be communicating over SSL. Even if you try to manually switch to not using SSL while you are logged in by removing the S from the protocol part of the URL, it'll still send you back to using HTTPS. This is the correct way of doing it. It's generally suggested that you not return to a non encrypted session after authenticating. This is typically to avoid session hijacking since your authentication cookie would never be sent over plain HTTP. Make sure the resources you have on external sources, are on sites that you trust. External resources access while in a HTTP connection should also be over an SSL connection. Again just because you communicate over SSL doesn't mean you're logged into those sources. For my app is 100% access over SSL, but I also have Google analytics and Google maps integration on the site (obviously both are external to my domain). I just make sure that I talk to Google over SSL. I don't have to actually be logged into Google to use any of those things. The same goes for your external images. Just make sure your URL's used to reference those external images are defined using the HTTPS moniker so that it uses SSL.

UPDATE: The reason you're getting two hits in the log like that is because you're login link is being requested over HTTP, the Require HTTPS attribute hits first realizes you're not using SSL and redirects you back to itself with the HTTPS protocol. If you update your ActionLink URL you can get around this, but as you know it gets ugly.

Url.Action("Login", "User", null, "https", Request.Url.Host)
0
Nikitesh On

Add this to your global.ascx

protected void Application_BeginRequest()
        {
            if (!Context.Request.IsSecureConnection)
                Response.Redirect(Context.Request.Url.ToString().Replace

("http:", "https:"));
        }

This will cause all the requestes to be converted to https instead of http

0
Raphaël On

I write an extension for Url.Action that generate the right protocol depending on the Attribute decooration. I let you check

public static class Extensions
{
    public static string SecuredAction(this UrlHelper helper, string action, string controller)
    {
        bool requireSSL = false;
        var route = System.Web.Routing.RouteTable.Routes.Select(r => r as System.Web.Routing.Route) .Where(r =>
            r != null && r.Defaults != null && r.Defaults["controller"] != null && r.Defaults["controller"].ToString().Equals(controller, StringComparison.InvariantCultureIgnoreCase)
            && r.Defaults["action"] != null && r.Defaults["action"].ToString().Equals(action, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();

        if(route == null)
            return helper.Action(action, controller);

        //Get the method and check if requiere ssl
        MemberInfo method = route.DataTokens["TargetActionMethod"] as MemberInfo;
        requireSSL = method.CustomAttributes.Any(a => a.AttributeType == typeof(RequireHttps)) || method.DeclaringType.CustomAttributes.Any(a => a.AttributeType == typeof(RequireHttps));

        //Return the correct protocol
        if(helper.RequestContext.HttpContext.Request.IsSecureConnection && !requireSSL)
            return helper.Action(action, controller, null, "http");

        if (!helper.RequestContext.HttpContext.Request.IsSecureConnection && requireSSL)
            return helper.Action(action, controller, null, "https");

        return helper.Action(action, controller);
    }
}