Skip to content

Instantly share code, notes, and snippets.

@brainded
Last active August 29, 2015 13:58
Show Gist options
  • Save brainded/9962987 to your computer and use it in GitHub Desktop.
Save brainded/9962987 to your computer and use it in GitHub Desktop.
Conditionally Require Https Attribute that allows a controller or controller action to force Https unless certain conditions are met to cancel the forward.
namespace MvcEssentials
{
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Web.Mvc;
/// <summary>
/// Conditionally Require Https Attribute
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class ConditionallyRequireHttps : System.Web.Mvc.RequireHttpsAttribute
{
/// <summary>
/// ConditionallyRequireHttpsEnabled Configuration
/// </summary>
/// <remarks>On by default if no configuration is found.</remarks>
public static readonly string ConditionallyRequireHttpsEnabled = ConfigurationManager.AppSettings["ConditionallyRequireHttpsEnabled"] ?? "true";
/// <summary>
/// Determines whether a request is secured (HTTPS) and, if it is not, calls the <see cref="M:System.Web.Mvc.RequireHttpsAttribute.HandleNonHttpsRequest(System.Web.Mvc.AuthorizationContext)" /> method.
/// </summary>
/// <param name="filterContext">An object that encapsulates information that is required in order to use the <see cref="T:System.Web.Mvc.RequireHttpsAttribute" /> attribute.</param>
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
// if the configuration override is present, disable the attribute
bool isRequireHttpsAttributeEnabled = false;
if (bool.TryParse(ConditionallyRequireHttpsEnabled, out isRequireHttpsAttributeEnabled))
{
if (!isRequireHttpsAttributeEnabled)
{
return;
}
}
// if the connection is already secure, do nothing
if (filterContext.HttpContext.Request.IsSecureConnection)
{
return;
}
/* You may not want this, but its here by example */
// if the connection is local, do nothing
if (filterContext.HttpContext.Request.IsLocal)
{
return;
}
// if the connection is secure from the originating proxy, do nothing
if (IsSecuredByOriginatingProxy(filterContext.HttpContext.Request.Headers))
{
return;
}
this.HandleNonHttpsRequest(filterContext);
}
/// <summary>
/// Determines whether [is secured by originating proxy] [the specified request].
/// </summary>
/// <param name="requestHeaders">The request headers.</param>
/// <returns>
/// <c>true</c> if [is secured by originating proxy] [the specified request]; otherwise, <c>false</c>.
/// </returns>
private static bool IsSecuredByOriginatingProxy(NameValueCollection requestHeaders)
{
//See http://en.wikipedia.org/wiki/List_of_HTTP_header_fields for header explanation
const string OriginatingProtocolHeader = "X-Forwarded-Proto";
//get the header value
string originatingProtocol = requestHeaders[OriginatingProtocolHeader];
//header not found
if (string.IsNullOrWhiteSpace(originatingProtocol)) return false;
//compare if the header is what was expected
bool isSecure = string.Equals(originatingProtocol, Uri.UriSchemeHttps, StringComparison.InvariantCultureIgnoreCase);
return isSecure;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment