MVC RequireHttps and redirect if not https

29.4k views Asked by At

I've read thru many of the questions on ASP.NET MVC [RequireHttps] - but can't find the answer to this question:

How do you make the [RequireHttps] attribute switch the url to https if it was not https to start with?

I have this code:

public ActionResult DoSomething()
{
    return View("AnotherAction");
}

[RequireHttps]
public ActionResult AnotherAction()
{
    return View();
}

But I get an error saying: "The requested resource can only be accessed via SSL."

The MVC futures project has a similar attribute [RequireSsl(Redirect = true)]. But that is outdated now ... What is the equivalent in MVC 2?

When someone types in the URL http://example.com/home/dosomething OR the url http://example.com/home/anotheraction, I need them to be automatically redirected to the url https://example.com/home/anotheraction

EDIT this is the sequence of events:

The URL http://example.com/home/dosomething is called from another website. They redirect their users to this url (with a response.redirect or similar).

DoSomething() then tries to return AnotherAction(), but fails with the error message "The requested resource can only be accessed via SSL."

5

There are 5 answers

2
Jeff Ogata On BEST ANSWER

The RequiresHttps attribute does automatically attempt to redirect to https://your-url. I verified this behavior on a site I have that uses that attribute, and also looking at the code in Reflector:

protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
    if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
    {
        throw new InvalidOperationException(MvcResources.RequireHttpsAttribute_MustUseSsl);
    }
    string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
    filterContext.Result = new RedirectResult(url);
}

Are you sure you have your site set up to accept secure connections? What happens if you try to browse to https://your-url directly?

2
Yakir Manor On

[mvc 4] short answer:

protected void Application_BeginRequest(Object source, EventArgs e)
{
  if (!Context.Request.IsSecureConnection)
  {
      Response.Redirect(Request.Url.AbsoluteUri.Replace("http://", "https://"));
  }
}

longer answer:
to move from http to https you cant send a redirect to https after the first packet,
therefor you need to catch the packet using Application_BeginRequest,
from the Global.asax add the function and it will override the default,
the code should be something like so (Global.asax on the class level):

protected void Application_BeginRequest(Object source, EventArgs e)
{
  if (!Context.Request.IsSecureConnection && 
      !Request.Url.Host.Contains("localhost") && 
      Request.Url.AbsolutePath.Contains("SGAccount/Login"))
  {
      Response.Redirect(Request.Url.AbsoluteUri.Replace("http://", "https://"));
  }
}

i strongly suggest putting a breakpoints and inspecting the Request.Url object for any url related need.
or visit the msdn page confused about request.url absoluteuri vs originalstring?
so am i you can go to dotnetperls for examples.
this function enables you to develop on localhost and deploying your code as is.
now for every page you want to make a https redirect you need to specify it in the if condition.
to move from https to http you can use regular Response.Redirect like so:

if (Request.Url.Scheme.Contains("https"))
{
    Response.Redirect(string.Format("http://{0}", Request.Url.Authority), true);
}

notice this also support working on the same code when developing on local host by not interrupting the original course of things pre the https addition.

also i recommend thinking about implementing some return url convention (if not already implemented) in that case you should go something like so:

if (Request.Url.Scheme.Contains("https"))
{
    Response.Redirect(string.Format("http://{0}{1}", Request.Url.Authority, returnUrl), true);
}

this will redirect to the requested page post login.

naturally you should protect every page that shows user data, register, login and more.

0
acarlon On

To supplement the answer already given, this is the code from the MVC 5 implementation of HandleNonHttpsRequest

protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
    // only redirect for GET requests, otherwise the browser might not propagate the verb and request
    // body correctly.
    ...
}
0
OzBob On

MVC4 does now redirect

but not how you would expect.

http://www.example.com:8080/alpha/bravo/charlie?q=quux will be redirect the client's browser to https://www.example.com/alpha/bravo/charlie?q=quux

Notice the lack of a port number.

http://aspnetwebstack.codeplex.com/SourceControl/latest#test/System.Web.Mvc.Test/Test/RequireHttpsAttributeTest.cs code test [Fact] public void OnAuthorizationRedirectsIfRequestIsNotSecureAndMethodIsGet()

confirms this is the desired behaviour.

If you would like to write a custom attribute that does include the PORT ... you can base your code on:

http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/RequireHttpsAttribute.cs

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class RequireHttpsAttribute : FilterAttribute, IAuthorizationFilter
{
    public RequireHttpsAttribute()
        : this(permanent: false)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="RequireHttpsAttribute"/> class.
    /// </summary>
    /// <param name="permanent">Whether the redirect to HTTPS should be a permanent redirect.</param>
    public RequireHttpsAttribute(bool permanent)
    {
        this.Permanent = permanent;
    }

    /// <summary>
    /// Gets a value indicating whether the redirect to HTTPS should be a permanent redirect.
    /// </summary>
    public bool Permanent { get; private set; }

    public virtual void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        if (!filterContext.HttpContext.Request.IsSecureConnection)
        {
            HandleNonHttpsRequest(filterContext);
        }
    }

    protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext)
    {
        // only redirect for GET requests, otherwise the browser might not propagate the verb and request
        // body correctly.

        if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException(MvcResources.RequireHttpsAttribute_MustUseSsl);
        }

        // redirect to HTTPS version of page
        string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
        filterContext.Result = new RedirectResult(url, this.Permanent);
    }
}
0
Aaron Sherman On

Http HEAD requests do not appear to be redirected. When reviewing our error logs we see lots of this message, googling lands here, but after looking more at the details they have a few interesting "features"

  • Request_method: HEAD
  • User Agent: curl/7.35.0

In other words all of the failed attempts were not customer facing...

(100% credit to comment from @arserbin3 for making me realize they were all HEAD requests)