14

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

1
  • You could write a custom attribute to do that. Commented Mar 27, 2011 at 5:00

5 Answers 5

24

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?

Sign up to request clarification or add additional context in comments.

2 Comments

actually as your code above shows, it only redirects for GET requests. If a POST (or HEAD, etc) request comes in, it will simply throw the Exception. An important distinction
Another consequence is that any controller action that is having [RequireHttps] and accessed via GET should also have [HttpGet] attribute to prevent the exception upon a HEAD request that is sent sometimes before/instead of its GET counterpart.
15

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

2 Comments

Looking for string "localhost" might not always work - it could be renamed. See this similar topic with my answer explaining how to handle HTTPS for production only with use of buil-in RequireHttpsAttribute inside Startup.cs (MVC 6).
Thanks for pointing out that code should be placed in "global.asax."
3

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)

Comments

2

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);
    }
}

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.