1

Based on the article Working with SSL in Web API I implemented an authorization filter to require SSL for a method of a Web API (2.1) Controller:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true,
                AllowMultiple = false)]
public sealed class RequireHttpsAttribute : AuthorizationFilterAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        if (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps)
        {
            actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden)
                                         {
                                             ReasonPhrase = "HTTPS Required"
                                         };
        }
        else
        {
            base.OnAuthorization(actionContext);
        }
    }
}

This works fine - on some web servers. If Web Farm Framework (WFF) is used as a reverse proxy, it can fail (by blocking valid HTTPS requests).

WFF adds the header X-Forwarded-Proto, which is a de facto standard for reverse proxies.

How can I revise this code to work with or without a standard proxy?

TrueWill
  • 25,132
  • 10
  • 101
  • 150
  • 1
    That kinda depends on the proxy, but most proxies will add some special headers or server variables (`HTTPS` and `HTTP_HTTPS` variables are popular) when the original request is using https. If you check the headers and server variables that your service gets on request behind the proxy, you should figure it out. Maybe this question would be helpful too: http://stackoverflow.com/questions/16330758/asp-net-web-api-how-do-you-read-server-variables-in-a-web-api-controller. – qbik Jul 09 '14 at 20:18

1 Answers1

1

Here's what I came up with:

/// <summary>
/// Action filter to require SSL for a protected resource.
/// </summary>
/// <remarks>
/// From http://www.asp.net/web-api/overview/security/working-with-ssl-in-web-api
/// but modified to support reverse proxies such as Web Farm Framework.
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public sealed class RequireHttpsAttribute : AuthorizationFilterAttribute
{
    [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Not possible.")]
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        if (IsSecure(actionContext.Request))
        {
            base.OnAuthorization(actionContext);
        }
        else
        {
            actionContext.Response =
                new HttpResponseMessage(HttpStatusCode.Forbidden)
                    {
                        ReasonPhrase = "HTTPS Required"
                    };
        }
    }

    private static bool IsSecure(HttpRequestMessage request)
    {
        if (request.RequestUri.Scheme == Uri.UriSchemeHttps)
        {
            return true;
        }

        IEnumerable<string> headerValues;

        if (request.Headers.TryGetValues("X-Forwarded-Proto", out headerValues))
        {
            string protocol = headerValues.FirstOrDefault();

            return string.Equals(protocol, "https", StringComparison.OrdinalIgnoreCase);
        }

        return false;
    }
}
TrueWill
  • 25,132
  • 10
  • 101
  • 150