tags:

views:

438

answers:

2

I am using Request.IsSecureConnection to check for SSL and redirecting where appropriate. When running my asp.net website on Rackspace's cloud, the server is running behind an SSL cluster, so IsSecureConnection will always return false. The same goes for checking whether the url contains "https://", always false, checking the port, etc. So the website gets stuck in big redirect loop.

Is there another way to check for SSL and redirect where appropriate? Anyone that has actually done this on Rackspace's cloud?

Public Class SecurityAwarePage
    Inherits Page

    Private _requireSSL As Boolean = False

    Public Property RequireSSL() As Boolean
        Get
            Return _requireSSL
        End Get
        Set(ByVal value As Boolean)
            _requireSSL = value
        End Set
    End Property

    Private ReadOnly Property IsSecure() As Boolean
        Get
            Return Request.IsSecureConnection
        End Get
    End Property

    Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
        MyBase.OnInit(e)

        PushSSL()
    End Sub

    Private Sub PushSSL()
        Const SECURE As String = "https://"
        Const UNSECURE As String = "http://"

        If RequireSSL AndAlso Not IsSecure Then
            Response.Redirect(Request.Url.ToString.Replace(UNSECURE, SECURE))
        ElseIf Not RequireSSL AndAlso IsSecure Then
            Response.Redirect(Request.Url.ToString.Replace(SECURE, UNSECURE))
        End If

    End Sub

End Class
+4  A: 

Although it is difficult to check if SSL is engaged a way around the problem is to force SSL.

From the RackspaceCloud Support knowledge base here: http://cloudsites.rackspacecloud.com/index.php/How_do_I_force_SSL_on_my_ASP/.NET_site%3F

You can re-write URLs in web.config:

<configuration>
<system.webServer>
  <rewrite>
    <rules>
      <rule name="Redirect to HTTPS" stopProcessing="true">
        <match url=".*" />
        <conditions>
          <add input="{HTTP_CLUSTER_HTTPS}" pattern="^on$" negate="true" />
          <add input="{HTTP_CLUSTER-HTTPS}" pattern=".+" negate="true" />
        </conditions>
        <action type="Redirect" url="https://{HTTP_HOST}{SCRIPT_NAME}" redirectType="SeeOther" />
      </rule>
    </rules>
  </rewrite>
</system.webServer>
</configuration>

You can force SSL in ASP.NET:

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&gt;

<script runat="server">
  protected void Page_Load(object sender, System.EventArgs e)
  {
    if(Request.ServerVariables["HTTP_CLUSTER_HTTPS"] != "on")
    {
      if(Request.ServerVariables.Get("HTTP_CLUSTER-HTTPS") == null)
      {
        string xredir__, xqstr__;

        xredir__ = "https://" + Request.ServerVariables["SERVER_NAME"];
        xredir__ += Request.ServerVariables["SCRIPT_NAME"];
        xqstr__ = Request.ServerVariables["QUERY_STRING"];

        if (xqstr__ != "")
            xredir__ = xredir__ + "?" + xqstr__;

        Response.Redirect(xredir__);
      }
    }
    Response.Write("SSL Only");
  }
</script>

<html>
<head id="Head1" runat="server">
  <title>SSL Only</title>
</head>
<body>
</body>
</html>
Kelly Orr
Thanks, I searched the help files and didn't find that myself. Looking back, I suppose it would have been smart to loop the ServerVariables collection and see what was there.
Charlie Brown
I'm curious, is "HTTP_CLUSTER-HTTPS" a typo? You have one with two underscores, and one with an underscore and a dash.
Chad Gorshing
A: 

I ran into this same problem with Rackspace Cloud and ended up solving it by manually implementing a Request.IsSecureConnection() extension method and replacing the framework's RequireHttpsAttribute with my own. Hopefully someone else will find this useful as well.

/// <summary>
/// Replaces framework-provided RequireHttpsAttribute to disable SSL requirement for local requests 
/// and properly enforce SSL requirement when used with Rackspace Cloud's load balancer
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class RequireHttpsAttribute : FilterAttribute, IAuthorizationFilter
{
    public virtual void OnAuthorization(AuthorizationContext filterContext) {
        if (filterContext == null) {
            throw new ArgumentNullException("filterContext");
        }

        if (filterContext.HttpContext.Request.IsLocal)
            return;

        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("The requested resource can only be accessed via SSL.");
        }

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

}

public static class Extensions {
    /// <summary>
    /// Gets a value which indicates whether the HTTP connection uses secure sockets (HTTPS protocol). Works with Rackspace Cloud's load balancer
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public static bool IsSecureConnection(this HttpRequestBase request) {
        const string rackspaceSslVar = "HTTP_CLUSTER_HTTPS";

        return (request.IsSecureConnection || (request.ServerVariables[rackspaceSslVar] != null || request.ServerVariables[rackspaceSslVar] == "on"));
    }

    /// <summary>
    /// Gets a value which indicates whether the HTTP connection uses secure sockets (HTTPS protocol). Works with Rackspace Cloud's load balancer
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public static bool IsSecureConnection(this HttpRequest request) {
        const string rackspaceSslVar = "HTTP_CLUSTER_HTTPS";

        return (request.IsSecureConnection || (request.ServerVariables[rackspaceSslVar] != null || request.ServerVariables[rackspaceSslVar] == "on"));
    }
}
Nathan Taylor