Https to Http redirect in asp.net - c#

We have a website and some of the pages are using https and those pages kept in magic folder.
https enabling and port number is configured in web.config for a site.
However if the user trying to access magic folder contents using http, we need to redirect back to https and vise versa
Ex Case 1: Working for http to https
http://mysite/magic-look to https://mysite/magic-look
Here, we used
<urlMappings>
<add url="~/magic-look" mappedUrl="~/magic/look.aspx"/>
<add url="~/help" mappedUrl="~/Help/default.aspx"/>
In Global.asax
protected void Application_BeginRequest(object sender, EventArgs e)
{
string url = HttpContext.Current.Request.Url.AbsoluteUri;
var secPort = String.IsNullOrEmpty(ConfigurationManager.AppSettings["securePort"]) ? 0 : Convert.ToInt32(ConfigurationManager.AppSettings["securePort"]);
var secProtocolEnabled = String.IsNullOrEmpty(ConfigurationManager.AppSettings["useSecure"]) ? false : true;
bool isSecureUrl = (url.IndexOf("/magic/", StringComparison.OrdinalIgnoreCase) >= 0) ? true : false;
if (url.IndexOf(".aspx", StringComparison.OrdinalIgnoreCase) >= 0)
{
url = url.Replace(":" + secPort, "");
if (isSecureUrl && secProtocolEnabled)
{
if (HttpContext.Current.Request.Url.Port != secPort)
{
//change .aspx page back to original SEO friendly URL and redirect
url = url.Replace(HttpContext.Current.Request.Url.AbsolutePath, HttpContext.Current.Request.RawUrl);
HttpContext.Current.Response.Redirect(Regex.Replace(url, "http", "https", RegexOptions.IgnoreCase));
}
}
else
{
if (HttpContext.Current.Request.Url.Port == secPort && !isSecureUrl)
{
//cause infinite loop
url = url.Replace(HttpContext.Current.Request.Url.AbsolutePath, HttpContext.Current.Request.RawUrl);
var targetUrl = Regex.Replace(url, "https", "http", RegexOptions.IgnoreCase);
HttpContext.Current.Response.Redirect(targetUrl);
}
}
}
}
Non https page accessed using Https, Not working, infinite loop
ISSUE: Not working for https to http
https://mysite/help to http://mysite/help
It gives infinite loop.. keep redirecting to *https://mysite/help*
https://mysite/help --> 302 Found
https://mysite/Help --> 302 Found
https://mysite/Help --> 302 Found
https://mysite/Help --> 302 Found .............
UPDATE:
If it remove this, it works fine.
url = url.Replace(HttpContext.Current.Request.Url.AbsolutePath,
HttpContext.Current.Request.RawUrl);
But 3 requests instead of 2
https://mysite/help --> 302 Found
https://mysite/Help/Default.aspx--> 302 Found
http://mysite/Help/Default.aspx--> 200 OK
However i want SEO friendly url like http://mysite/Help/
UPDATE 2: ROOT CAUSE:
Whenever the url is https://../something and redirecting to http://../something is always making request https://../something

I just re-factored your code and used string.Format method to build url
private static string GetMyUrl(string securePort, string useSecure, Uri uri)
{
int secPort;
if (!int.TryParse(securePort, out secPort)) secPort = 0;
bool secProtocolEnabled = !string.IsNullOrWhiteSpace(useSecure);
bool shouldBeSecureUrl = uri.AbsolutePath.Contains("/magic/");
if (!uri.AbsolutePath.EndsWith(".aspx")) return uri.AbsoluteUri;
bool forceSecure = (shouldBeSecureUrl && secProtocolEnabled);
string url = string.Format("{0}://{1}{2}{3}",
forceSecure
? "https"
: "http",
uri.Host,
forceSecure && secPort != 0
? string.Format(":{0}", secPort)
: string.Empty,
uri.PathAndQuery);
return url;
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
string url = GetMyUrl(ConfigurationManager.AppSettings["securePort"],
ConfigurationManager.AppSettings["useSecure"], HttpContext.Current.Request.Url);
if (HttpContext.Current.Request.Url.AbsoluteUri != url) HttpContext.Current.Response.Redirect(url);
}
You can test your addresses. Samples...
Console.WriteLine(GetMyUrl("8484","t", new Uri("https://www.contoso.com/catalog/shownew.aspx?date=today")));
Console.WriteLine(GetMyUrl("8484","t", new Uri("https://www.contoso.com:8484/magic/shownew.aspx?date=today")));
Console.WriteLine(GetMyUrl("8484","t", new Uri("http://www.contoso.com:8080/magic/shownew.aspx?date=today")));
Console.WriteLine(GetMyUrl("","t", new Uri("https://www.contoso.com/catalog/shownew.aspx?date=today")));
Console.WriteLine(GetMyUrl("8484","", new Uri("https://www.contoso.com/catalog/shownew.aspx?date=today")));

Finally found that we have url rewrite enabled, so just added config entry, works for all scenario
<configSections>
<section name="URLRewriteConfiguration"
type="MyAssembly.Routing.URLRewriteConfiguration, MyAssembly.Routing,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</configSections>
<URLRewriteConfiguration>
<Routes>
<add Pattern="/help/default.aspx" PermanentRedirect="true" Replacement="/help"/>
and removed
url = url.Replace(HttpContext.Current.Request.Url.AbsolutePath, HttpContext.Current.Request.RawUrl);

Related

Secure flag not set to Cookies in .Net application

I have included below lines of code in my Web.Config and Global.asax.cs file. Still when I use developer tools in browser I could see secure flag not set to the below Cookies.
Also Configured SSLSettings in my IIS(Selected checkbox requireSSL).
I would like to set Secure attribute to all Cookies not only to received but also to Sent cookies. Any suggestion please.
In Web.config:
<httpCookies requireSSL="true"/>
In Global.asax.cs:
protected void Application_EndRequest(object sender, EventArgs e)
{
if (Request.IsSecureConnection == true && HttpContext.Current.Request.Url.Scheme == "https")
{
Request.Cookies["ASP.NET_SessionID"].Secure = true;
if (Request.Cookies.Count > 0)
{
foreach (string s in Request.Cookies.AllKeys)
{
Request.Cookies[s].Secure = true;
}
}
Response.Cookies["ASP.NET_SessionID"].Secure = true;
if (Response.Cookies.Count > 0)
{
foreach (string s in Response.Cookies.AllKeys)
{
Response.Cookies[s].Secure = true;
}
}
}
}
In Browser:
There are two ways, one httpCookies element in web.config allows you to turn on requireSSL which only transmit all cookies including session in SSL only and also inside forms authentication, but if you turn on SSL on httpcookies you must also turn it on inside forms configuration too.
<form>
<httpCookies requireSSL="true" />

Request.Url.Scheme gives http instead of https on load balanced site

I am testing a new load balanced staging site and the https is set up at the load balancer level, not at the site level. Also, this site will be always https so i don't need remote require https attributes etc. The url displays https but it is not available in my code. I have a few issues due to this reason
Request.Url.Scheme is always http:
public static string GetProtocol()
{
var protocol = "http";
if (HttpContext.Current != null && HttpContext.Current.Request != null)
{
protocol = HttpContext.Current.Request.Url.Scheme;
}
return protocol;
}
Same thing with this base url, protocol is http
public static string GetBaseUrl()
{
var baseUrl = String.Empty;
if (HttpContext.Current == null || HttpContext.Current.Request == null || String.IsNullOrWhiteSpace(HttpRuntime.AppDomainAppPath)) return baseUrl;
var request = HttpContext.Current.Request;
var appUrl = HttpRuntime.AppDomainAppVirtualPath;
baseUrl = string.Format("{0}://{1}{2}", request.Url.Scheme, request.Url.Authority, appUrl);
if (!string.IsNullOrWhiteSpace(baseUrl) && !baseUrl.EndsWith("/"))
baseUrl = String.Format("{0}/", baseUrl);
return baseUrl;
}
Now the biggest issue is referencing js files and google fonts referenced in the style sheets. I am using // here without http or https but these are treated as http and i see mixed content blocked message in FireBug.
How can i overcome this issue?
As you've said HTTPS termination is done at load balancer level ("https is set up at the load balancer level") which means original scheme may not come to the site depending on loadbalancer configuration.
It looks like in your case LB is configured to talk to site over HTTP all the time. So your site will never see original scheme on HttpContext.Request.RawUrl (or similar properties).
Fix: usually when LB, proxy or CDN configured such way there are additional headers that specify original scheme and likely other incoming request parameters like full url, client's IP which will be not directly visible to the site behind such proxying device.
I override the ServerVariables to convince MVC it really is communicating through HTTPS and also expose the user's IP address. This is using the X-Forwarded-For and X-Forwarded-Proto HTTP headers being set by your load balancer.
Note that you should only use this if you're really sure these headers are under your control, otherwise clients might inject values of their liking.
public sealed class HttpOverrides : IHttpModule
{
void IHttpModule.Init(HttpApplication app)
{
app.BeginRequest += OnBeginRequest;
}
private void OnBeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
string forwardedFor = app.Context.Request.Headers["X-Forwarded-For"]?.Split(new char[] { ',' }).FirstOrDefault();
if (forwardedFor != null)
{
app.Context.Request.ServerVariables["REMOTE_ADDR"] = forwardedFor;
app.Context.Request.ServerVariables["REMOTE_HOST"] = forwardedFor;
}
string forwardedProto = app.Context.Request.Headers["X-Forwarded-Proto"];
if (forwardedProto == "https")
{
app.Context.Request.ServerVariables["HTTPS"] = "on";
app.Context.Request.ServerVariables["SERVER_PORT"] = "443";
app.Context.Request.ServerVariables["SERVER_PORT_SECURE"] = "1";
}
}
void IHttpModule.Dispose()
{
}
}
And in Web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="HttpOverrides" type="Namespace.HttpOverrides" preCondition="integratedMode" />
</modules>
</system.webServer>
I know this is an old question, but after encountering the same problem, I did discover that if I look into the UrlReferrer property of the HttpRequest object, the values will reflect what was actually in the client browser's address bar.
So for example, with UrlReferrer I got:
Request.UrlReferrer.Scheme == "https"
Request.UrlReferrer.Port == 443
But for the same request, with the Url property I got the following:
Request.Url.Scheme == "http"
Request.Url.Port == 80
According to https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer
When HTTPS requests are proxied over HTTP, the original scheme (HTTPS)
may be lost and must be forwarded in a header.
In Asp.Net Core I found ( not sure if it works for all scenarios) that even if request.Scheme is misleadingly shows “http” for original “https”, request.IsHttps property is more reliable.
I am using the following code
//Scheme may return http for https
var scheme = request.Scheme;
if(request.IsHttps) scheme= scheme.EnsureEndsWith("S");
//General string extension
public static string EnsureEndsWith(this string str, string sEndValue, bool ignoreCase = true)
{
if (!str.EndsWith(sEndValue, CurrentCultureComparison(ignoreCase)))
{
str = str + sEndValue;
}
return str;
}

Having an ASMX web service inside a dotnetnuke module

I have implemented a DNN module that works fine. Now my employer wants me to expose some of the features as web services.I want to keep everything clean and simple so I decided to add an asmx web service to my module.
The web service itself was working fine but when I tried to add a HttpModule to handle request authentication everything was messed up.
I have an event handler for AuthenticateRequest as follow :
private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
{
var app = sender as HttpApplication;
if (app == null || !(app.Request.Url.ToString().EndsWith("WebServices.asmx"))) return;
var request = app.Request;
var authHeader = request.Headers["Authorization"];
if (authHeader != null)
{
var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);
// RFC 2617 sec 1.2, "scheme" name is case-insensitive
if (authHeaderVal.Scheme.Equals("basic",
StringComparison.OrdinalIgnoreCase) &&
authHeaderVal.Parameter != null)
{
AuthenticateUser(authHeaderVal.Parameter);
}
}
else
{
HttpContext.Current.Response.StatusCode = 401;
}
}
and here's the client side :
var service = new localhost.WebServices();
var credentialsCache = new CredentialCache
{
{new Uri(service.Url), "Basic", new NetworkCredential("user", "password")}
};
service.Credentials = credentialsCache;
Console.WriteLine(service.HelloWorld());
The problem is no Authorization header is added to my request this way. I searched and I found out that there was a round trip here that the header was not added for the first time and if response had a basic authorization header ,another request was sent with the header. Thus I added the else part :
else
{
HttpContext.Current.Response.StatusCode = 401;
}
and an EndRequest handler :
private static void OnApplicationEndRequest(object sender, EventArgs e)
{
var response = HttpContext.Current.Response;
if (response.StatusCode == 401)
{
response.Headers.Add("WWW-Authenticate",
string.Format("Basic realm=\"{0}\"", Realm));
}
}
Now every time I send a request I am redirected to login page !!!(I know that this happens because there is another FormAuthentication HttpModule that redirects the request to login page if StatusCode is 401)
Please tell me what am I doing wrong ?(is there a way to prevent the other HttpModule to interfere with my response ? )
UPDATE
I set PreAuthenticate property to true but still no authorization header is sent

Facebook C# SDK - Problem with Iframe app and Safari Cookies

I'm building an Iframe canvas application for Facebook. I'm not using the Javascript SDK.
This is the code I'm using, and it works well in all browsers except for Safari.
protected FacebookApp app;
protected CanvasAuthorizer cauth;
Response.AddHeader("p3p", "CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"");
app = new FacebookApp();
cauth = new CanvasAuthorizer(app);
if (!cauth.IsAuthorized())
{
myAuth auth = new myAuth();
myAuth.Authorize(app, Request, Response, perms);
}
if (cauth.Authorize())
{
// Do my app stuff here
}
public class myAuth
{
public static void Authorize(FacebookApp fbApp, System.Web.HttpRequest request, System.Web.HttpResponse response, string perms)
{
Authorize(fbApp, request, response, perms, null);
}
public static void Authorize(FacebookApp fbApp, System.Web.HttpRequest request, System.Web.HttpResponse response, string perms, string redirectUrl)
{
Uri url = fbApp.GetLoginUrl();
NameValueCollection nvc = System.Web.HttpUtility.ParseQueryString(url.Query);
if (!string.IsNullOrEmpty(perms))
nvc.Add("req_perms", perms);
if (!string.IsNullOrEmpty(redirectUrl))
nvc["next"] = GetAppRelativeUrl(redirectUrl);
else if (request.QueryString.Count > 0)
nvc["next"] = GetAppRelativeUrl(request.Path.Replace(request.ApplicationPath, string.Empty).Replace(request.ApplicationPath.ToLower(), string.Empty) + "?" + request.QueryString);
else
nvc["next"] = GetAppRelativeUrl(request.Path.Replace(request.ApplicationPath, string.Empty).Replace(request.ApplicationPath.ToLower(), string.Empty));
UriBuilder ub = new UriBuilder(url);
ub.Query = nvc.ToString();
string content = CanvasUrlBuilder.GetCanvasRedirectHtml(ub.Uri);
response.ContentType = "text/html";
response.Write(content);
response.End();
}
public static string GetAppRelativeUrl(string url)
{
return CanvasSettings.Current.CanvasPageUrl.ToString();
}
}
I read about Safari not allowing third party cookies, and I figure that's where the problem lies. My question is wheter there's a way to handle this using the SDK, or what my options are.
Regards,
Anders Pettersson
I've had some problems with Safari changing the case of data sent in HTTP headers... make sure any parsing/comparing you are doing is case insensitive.
See here: Facebook Iframe App with multiple pages in Safari Session Variables not persisting
here I am getting same problem but now I got solution for safari..
Just change validation mode in web.config
<system.web>
<pages enableViewState="true" validateRequest="false" />
<httpRuntime requestValidationMode="2.0"/>
<!--
Enable this code if you get still problem
<sessionState cookieless="true" regenerateExpiredSessionId="true" />-->

remove default.aspx from a request

i am trying to remove default.aspx from any request that might have it.
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
string url = context.Request.Url.ToString();
// remove default.aspx
if (url.EndsWith("/default.aspx", StringComparison.OrdinalIgnoreCase))
{
url = url.Substring(0, url.Length - 12);
context.Response.Redirect(url);
}
}
gives an error:
**too many redirects occurred trying to open...**
what can i change to make it work?
thnx
k got it.
instead of using:
string url = context.Request.Url.ToString();
i tried:
string url = context.Request.RawUrl.ToString();
and that WORKS! together with what you guys said :)
I think that if you put the redirect inside the if you don't have to deal with infinite redirects.
You are endlessly redirecting.
Each time the following line executes the Application_BeginRequest event is fired again.
context.Response.Redirect(url);
Put the redirect inside the if statement like this.
if (url.EndsWith("/default.aspx", StringComparison.OrdinalIgnoreCase))
{
url = url.Substring(0, url.Length - 12);
context.Response.Redirect(url);
}

Categories

Resources