I am wanting to redirect a page to a secure connection for an ASPX file.
Clients are asked to copy and paste a URL that looks like this foo.com.au into the browser.
I have this code below working on the code behind file but am wondering when it is deployed to production if this will update the URL to have www after the https://www as the URL provided to clients does not have www in it?
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
if (!Request.IsLocal && !Request.IsSecureConnection)
{
string redirectUrl = Request.Url.ToString().Replace("http:", "https:");
Response.Redirect(redirectUrl);
}
}
Rather than using Request.Url, use Request.Url.AbsoluteUri. In addition, you should not assume that the URL will be entered in lowercase. I would revise the code to be:
if (!Request.IsLocal && !Request.IsSecureConnection)
{
if (Request.Url.Scheme.Equals(Uri.UriSchemeHttp, StringComparison.InvariantCultureIgnoreCase))
{
string sNonSchemeUrl = Request.Url.AbsoluteUri.Substring(Uri.UriSchemeHttp.Length);
// Ensure www. is prepended if it is missing
if (!sNonSchemeUrl.StartsWith("www", StringComparison.InvariantCultureIgnoreCase)) {
sNonSchemeUrl = "www." + sNonSchemeUrl;
}
string redirectUrl = Uri.UriSchemeHttps + sNonSchemeUrl;
Response.Redirect(redirectUrl);
}
}
If you do this, all it will change is the schema. So, if the absoluteUri is
http://foo.com.au
it will be changed to
https://foo.com.au
One last note: when we have done this, we have never tried it in OnPreInit, we always perform this logic in Page_Load. I am not sure what, if any, ramifications there will be for redirecting at that portion of the page lifecycle, but if you run into issues, you could move it into Page_Load.
This was my final implementation to account for a request comes through for https://foo and not https://www.foo
if (!Request.IsLocal &&
!Request.Url.AbsoluteUri.StartsWith("https://www.", StringComparison.OrdinalIgnoreCase))
{
string translatedUrl;
string nonSchemeUrl = Request.Url.AbsoluteUri;
string stringToReplace = (Request.Url.Scheme == Uri.UriSchemeHttp ? Uri.UriSchemeHttp + "://" : Uri.UriSchemeHttps + "://");
nonSchemeUrl = nonSchemeUrl.Replace(stringToReplace, string.Empty);
if (!nonSchemeUrl.StartsWith("www", StringComparison.InvariantCultureIgnoreCase))nonSchemeUrl = "www." + nonSchemeUrl;
translatedUrl = Uri.UriSchemeHttps + "://" + nonSchemeUrl;
Response.Redirect(nonSchemeUrl);
}
Related
My requirement is to redirect user to new url from old url . My entity is as follows:
public class HttpRedirect
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Old url is required.")]
[MaxLength(1000)]
//[Url]
[Display(Name = "Old Url")]
public string OldUrl { get; set; }
[Required(ErrorMessage = "New url is required.")]
[MaxLength(1000)]
//[Url]
[Display (Name = "New Url")]
public string NewUrl { get; set; }
}
Url is commented so that I can work in localhost.
So when Old url is requested I want the request is transferred to new url
To achieve this in global.asax file in Application_BeginRequest event I have following code
protected void Application_BeginRequest(object sender, EventArgs e)
{
//You don't want to redirect on posts, or images/css/js
bool isGet = HttpContext.Current.Request.RequestType.ToLowerInvariant().Contains("get");
if (isGet && HttpContext.Current.Request.Url.AbsolutePath.Contains(".") == false)
{
string lowercaseURL = (Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority + HttpContext.Current.Request.Url.AbsolutePath);
string newUrl = new HttpRedirectRepository().RedirectUrl(lowercaseURL);
if(newUrl != null)
{
lowercaseURL = newUrl;
}
lowercaseURL = lowercaseURL.ToLower().Trim() + HttpContext.Current.Request.Url.Query;
Response.Clear();
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", lowercaseURL);
Response.End();
}
}
I have implemented the above code to accomplish 2 task.
1. Change url to lower case
2. If new url is available for requested url redirect to that url.
With my above implementation I have strong feeling that its working but causing infinite loop by redirecting to a lowercaseURL
So how can I prevent multiple redirection. For e.g.
I request http://localhost:80/mypage and I have set its new url to http://localhost:80/home then when mypage is requested it should redirect to home making url in lowercase and redirect should occur only one time.
note
I need to redirect within my own domain only.
User will enter full url address for both old and new url.
UPDATE
With some hint from #RobertHarvey I have modified my code as follows which is working for me
protected void Application_BeginRequest(object sender, EventArgs e)
{
//You don't want to redirect on posts, or images/css/js
bool isGet = HttpContext.Current.Request.RequestType.ToLowerInvariant().Contains("get");
if (isGet && HttpContext.Current.Request.Url.AbsolutePath.Contains(".") == false)
{
bool redirect = false;
string requestUrl = (Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority + HttpContext.Current.Request.Url.AbsolutePath);
//You don't want to change casing on query strings
string newUrl = new HttpRedirectRepository().RedirectUrl(requestUrl);
if (newUrl != null)
{
requestUrl = newUrl;
redirect = true;
}
if (Regex.IsMatch(requestUrl, #"[A-Z]"))
{
requestUrl = requestUrl.ToLower().Trim() + HttpContext.Current.Request.Url.Query;
redirect = true;
}
if (redirect)
{
Response.Clear();
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", requestUrl);
Response.End();
}
}
}
Though still I believe updated implementation has some limitation. I would appreciate any further code enhancement and case coverage.
Your two "notes" are contradictory. If you only want to allow redirection within your domain, then recording a full URL, including a domain, is entirely unnecessary, and only serves to complicate things. Make the old/new URLs just absolute paths. This also solves your problem with working in development vs. production, since you don't need to worry about the data having the right domain for the right environment.
Note: your infinite redirect is a bug in your code here. You should be redirecting to newUrl, not lowercaseUrl. In the case where newUrl is null, there is no redirect, so you should simply not redirect at all in that case.
I have a situation where I need to restrict a user to enter a Url which already exits in DB.
Here is the function that I am using to validate:
public bool IsContentUrlExists(string url)
{
url = url.Trim().TrimEnd(new[]{'/'});
return Context.Contents.Any(content => content.Url == url);
}
With this method I can validate for a Url say "/testurl/" that matches a url "/testurl" in DB.
But otherway it will not work when I go to compare "/testurl" string with "/testurl/" in DB.
I need to remove the trailing slash in both case but TrimEnd(new[]{'/'}) will not work on a column in EF query. So the following method will fail
public bool IsContentUrlExists(string url)
{
url = url.Trim().TrimEnd(new[]{'/'});
return Context.Contents.Any(content => content.Url.Trim().TrimEnd(new[]{'/'}) == url);
}
Can anyone help me with an alternative solution?
N.B: We don't have any standard for URL in our existing DB
Using your code plus mine
public bool IsContentUrlExists(string url)
{
url = url.Trim().TrimEnd(new[]{'/'});
return Context.Contents.Any(content => content.Url == url || content.Url == url + "/");
}
Untested but something like the above shoudl work, shouldn't it?
Wing
As an alternate you can try:
url = url.Trim().TrimEnd(new[] { '/' });
var lstUrls = new List<string> { url, url + "/" };
return Context.Contents.Any(content => lstUrls.Contains(content.Url));
It compares a list of two strings: one ending with slash and the other without.
If there will be a match for any of these two strings in database then it means the URL exists!
I have my local host and a live site. I have a url and if its in localhost the url should go localhost/site/thank_you.aspx and if its live http://mylivesite.com/thank_you.aspx
I have tried this in my code behind...
MyHiddenField.Value = Request.URL + "/thank_you.aspx";
but it returned the page I was on /thank_you.aspx
What am I doing wrong?
Try this, I even added scheme in too, just in case you go https :)
EDIT: Also added port (Thanks Alex) in order to be super duper super future-proof :)
MyHiddenField.Value = string.Format(
"{0}://{1}{2}/thank_you.aspx",
Request.Url.Scheme,
Request.Url.Host,
Request.Url.IsDefaultPort ? string.Empty : ":" + Request.Url.Port);
EDIT: Another good suggestion by #MikeSmithDev, put it in a function
public string GetUrlForPage(string page)
{
return MyHiddenField.Value = string.Format(
"{0}://{1}{2}/{3}",
Request.Url.Scheme,
Request.Url.Host,
Request.Url.IsDefaultPort ? string.Empty : ":" + Request.Url.Port,
page);
}
Then you can do:
MyHiddenField.Value = GetUrlForPage("thank_you.aspx");
There is a built-in class UriBuilder
var url = Request.Url;
var newurl = new UriBuilder(url.Scheme, url.Host, url.Port, "thank_you.aspx")
.ToString();
Adding to the answers above. Allow function to handle relative paths. For example: ~/ or ~/test/default.aspx
public string GetUrlForPage(string relativeUrl)
{
return string.Format(
"{0}://{1}{2}{3}",
Request.Url.Scheme,
Request.Url.Host,
Request.Url.IsDefaultPort ? string.Empty : ":" + Request.Url.Port,
Page.ResolveUrl(relativeUrl));
}
i have an asp.net website where i need to use URL re-write so i have written an HTTP module and i have implemented it and it works correctly the only problem is when the page redirect to its corresponding address the images and the styles are not loaded.
here is the http module:
// Your BeginRequest event handler.
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
string URL = application.Request.Url.ToString();
//int pid = Convert.ToInt32(application.Request.QueryString["pid"]);
if ((URL.ToLower().Contains(".aspx"))
|| (URL.ToLower().Contains(".js"))
|| (URL.ToLower().Contains(".css"))
|| (URL.ToLower().Contains(".gif"))
|| (URL.ToLower().Contains(".png"))
|| (URL.ToLower().Contains(".jpeg"))
|| (URL.ToLower().Contains(".jpe"))
|| (URL.ToLower().Contains(".jpg"))
|| (URL.ToLower().Contains(".ashx")))
return;
else
{
string mname = URL.Substring(URL.LastIndexOf("/") + 1).ToString();
Merchand ms = merchantDB.GetMerchant(mname);
HttpContext context = application.Context;
if (ms != null)
{
string url = "~/pages/Merchant.aspx?mid=" + ms.MerchandID + "&catid=" + ms.MainCategory + "&subcatid=0";
context.RewritePath(VirtualPathUtility.ToAppRelative(url));
}
else
{
//("");
string url = "~/pages/default.aspx";
context.RewritePath(VirtualPathUtility.ToAppRelative(url));
}
}
}
when i open the page from it normal URL it opens fine, but when i use the url rewrite it open but with out images or styles.
when i open firebug i get an error that the css and the javascript are not found
To make rewrite work with IIS, do the following:
Register the httpmodule in web.config in the system.webserver tag:
<modules>
<add name="Rewrite" type="Rewrite"/>
</modules>
change this: context.RewritePath(VirtualPathUtility.ToAppRelative(url)); to this: context.RewritePath(url, false);
make your images runat server and put their path as ~/images/imagename
i found this great little HttpModule that encrypts and decrypts all querystrings. It can be found here: HttpModule for query string encryption
There is one major flaw that i could really use some input on how to solve. On a postback of the page the HttpMethod POST gets skipped and the QueryString gets shown decrypted. Obviously this is a major security risk.
void context_BeginRequest(object sender, EventArgs e)
{
try
{
HttpContext context = HttpContext.Current;
if (context.Request.Url.OriginalString.Contains("aspx") && context.Request.RawUrl.Contains("?"))
{
string query = ExtractQuery(context.Request.RawUrl);
string path = GetVirtualPath();
if (query.StartsWith(PARAMETER_NAME, StringComparison.OrdinalIgnoreCase))
{
// Decrypts the query string and rewrites the path.
string rawQuery = query.Replace(PARAMETER_NAME, string.Empty);
string decryptedQuery = Decrypt(rawQuery);
context.RewritePath(path, string.Empty, decryptedQuery);
}
else if (context.Request.HttpMethod == "GET")
{
// Encrypt the query string and redirects to the encrypted URL.
// Remove if you don't want all query strings to be encrypted automatically.
string encryptedQuery = Encrypt(query);
context.Response.Redirect(path + encryptedQuery);
}
}
}
catch (ThreadAbortException)
{
//do nothing. let it pass
}
catch (Exception exc)
{
ReportError(exc);
}
}
I tried putting a addition if catch for the POST method:
else if (context.Request.HttpMethod == "POST")
{
if (!query.StartsWith(PARAMETER_NAME, StringComparison.OrdinalIgnoreCase))
{
string encryptedQuery = Encrypt(query);
context.Response.Redirect(path + encryptedQuery);
}
}
However this reloads the page becuase of the Response.Redirect and so the PostBack is useless.
Does anyone have any ideas or know if there is a way to determine is the HttpContext is a PostBack?
Sending sensitive data in the querystring is not a good idea. If you have to then better to encrypt the data before building your querystring rather than encrypting the whole querystring. Also your site should not be compromised by a user changing the querystring. URI takes a user to where he wants to go so navigating by changing your querystring (URI) is a standard for the web. The web should be RestFul.