2

I have built an application on asp.net 3.5 which is hosted on AppHarbor. The problem is that on HTTPS URL rewriting is not working. The following is the code to run some of the pages on SSL:

string CurrentUrl = Request.Url.ToString();
string sPath = System.Web.HttpContext.Current.Request.Url.AbsolutePath;      
System.IO.FileInfo oInfo = new System.IO.FileInfo(sPath);
string sRet = oInfo.Name;       
string sDir = oInfo.Directory.Name;     
pageName = sRet;
if (sRet == "Register.aspx" || sRet == "Login.aspx" || sRet == "Post.aspx" || sRet == "ChangePassword.aspx" || sRet == "ChangeUserStatus.aspx" || sRet == "Verification.aspx" || sRet == "ContactInfo.aspx" || sRet == "Find.aspx" || sRet == "MyAccount.aspx" || sRet == "MyEmailAddresses.aspx" || sRet == "Load.aspx" || sRet == "MyPostedLoads.aspx" || sRet == "MySubmittedBids.aspx" || sRet == "MySavedAddresses.aspx" || sRet == "MyCarriers.aspx" || sRet == "MyPotentialLoads.aspx" || sRet == "MyFreightAlarms.aspx" || sRet == "MyFreightAlarmsPreferences.aspx" || sRet == "MyAddress.aspx" || sRet == "GetUserComments.aspx" || sRet == "MyCreditCard.aspx" || sRet == "MyWallet.aspx" || sRet == "InvoiceMe.aspx" || sRet == "MyShippers.aspx" || sRet == "MyCoWorkers.aspx" || sRet == "MyACH.aspx" || sRet == "RouteMap.aspx" || sRet == "Pricing.aspx" || sRet == "PricingPayment.aspx" || sRet == "PaymentProcessed.aspx")
{
    string NewUrl = "";

    if (!Request.IsSecureConnection && !string.Equals(HttpContext.Current.Request.Headers["X-Forwarded-Proto"], "https", StringComparison.OrdinalIgnoreCase))
    {
        NewUrl = Regex.Replace(CurrentUrl,
                               @"^https?(://[^/:]*)(:\d*)?", 
                               "https$1", 
                               RegexOptions.IgnoreCase);

        Response.Redirect(NewUrl);
    }
}

And the rule for URL rewrite on web.config:

<rewrite>
  <rules>
    <rule name="Rewrite with .aspx" stopProcessing="true">
      <match url="^([^\.]+)$" />
      <action type="Rewrite" url="{R:1}.aspx" />
    </rule>
    <rule name="Redirect .aspx page requests" stopProcessing="true">
      <match url="(.+)\.aspx" />
      <action type="Redirect" url="{R:1}" />
    </rule>
  </rules>
</rewrite>

The problem is the that page remains in an indefinite loop and can not redirect properly.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • Are you able to debug and set breakpoints against the code running locally to test the values for `NewUrl` before redirect? It also looks like redirect will always happen if `X-Forwarded-Proto` is not set. Is this header value always assured to be set? – Mike Guthrie Jul 16 '12 at 20:44

2 Answers2

0

RequireHttpsAttribute: If you're using the built-in RequireHttpsAttribute to ensure that a controller action always uses HTTPS you will experience a redirect loop. The reason is that SSL is terminated at the load balancer level and RequireHttps doesn't recognize the X-Forwarded-Proto header it uses to indicate that the request was made using HTTPS.

The "meat" is in bold.

See: AppHarbor SSL FAQ - specifically the Troubleshooting section.

It's the same issue if you have an SSL concentrator or similar device in front of your web server(s). It's quite common in "cloud hosting" environments....

Hth....

EdSF
  • 11,753
  • 6
  • 42
  • 83
  • SSL is working fine in our application without Url Rewriting but when we apply the above url rewrite rule then application goes in indefinite loop and browsers detect that page is not redirect properly..That is the main problem – user1529681 Jul 16 '12 at 22:37
0

Like EdSF mentioned, the problem you are experiencing is because the SSL (HTTPs) is at the load-balancer level. Meaning, all requests that come into your ASP.NET application will be HTTP.

So in your application running on AppHarbor, the following will always be true:

Request : https://mysite.com/about
---------------------------------------------------------------------------------
-> Request.Url.Scheme // http
-> Request.Url.AbsoluteUri // http://mysite.com:port/about
-> Request.issecure // false

You rewrite rules are relying on the protocal/scheme to be https and it never will be, causing an infinite loop.

The way to check for HTTPS in your ASP.NET application running on AppHarbor would be the following:

string.Equals(Request.Headers["X-Forwarded-Proto"], 
              "https", 
              StringComparison.InvariantCultureIgnoreCase);

I also host my web application on AppHarbor and needed a better solution so I created the SecurePages project (NuGet - GitHub). This project allows you to configure secure/https URLs with string literals as well as regex. It also forces all other URLs to use HTTP. You can also register custom predicate delegates that act as HTTPs request matching rules. So you could register one for AppHarbor to check the headers:

//Secure a page    
secureUrls.AddUrl("/Register.aspx");

//Secure any page under /cart/*
secureUrls.AddRegex(@"(.*)cart", RegexOptions.IgnoreCase);

//Register a custom HTTPs match rule for AppHarbor
SecurePagesConfiguration.RegisterCustomMatchRule(
                c => string.Equals(c.Request.Headers["X-Forwarded-Proto"], "https", StringComparison.InvariantCultureIgnoreCase));

Secure pages also supports Unit Testing and local browser testing with IIS Express.

Paul
  • 12,392
  • 4
  • 48
  • 58