ASP.NET : Hide Querystring in URL - asp.net

I don't know if I'm just being overly hopeful, but is there a way to hide the query string returned in the URL?
The scenario I am in is where I have page1.aspx redirecting a command to an outside server via a post, and it returns it to page2.aspx. The only problem I have with this, is that the querystring of the returned variables are still left in the URL.
I just want to hide the ugly string/information from the common user. So is there a way to edit and reload that in the pageload method or do I just have to save the variables on a middleman page and then hit page 2.

What is the origin of these querystring variables? Can you not submit all data as POST data, so that there is no querystring?

You could possibly also use
Context.RewritePath("/foo.aspx")
Here's a link to a ScottGu blog post about URL rewriting.
http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx

Awhile back I made some http encoding encrypt/decrypt methods for this purpose. Sometimes in asp.net you need to use the query string, but you also need the end user to not know the value. What I do is base 64 encode, encrypt the value, hash the value based on my private key, and stick them together with a -. On the other side I check the left side hash to verify authenticity, and decrypt the right side. One really nice gotcha is that + (which is a valid base64 string value) is equal to space in html encoding, so I take that into account in the decrypt.
The way I use this is add the encrypted value to the query string, and then decrypt it on the other side
private const string KEY = "<random value goes here>";
public static string EncryptAndHash(this string value)
{
MACTripleDES des = new MACTripleDES();
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
des.Key = md5.ComputeHash(Encoding.UTF8.GetBytes(KEY));
string encrypted = Convert.ToBase64String(des.ComputeHash(Encoding.UTF8.GetBytes(value))) + '-' + Convert.ToBase64String(Encoding.UTF8.GetBytes(value));
return HttpUtility.UrlEncode(encrypted);
}
/// <summary>
/// Returns null if string has been modified since encryption
/// </summary>
/// <param name="encoded"></param>
/// <returns></returns>
public static string DecryptWithHash(this string encoded)
{
MACTripleDES des = new MACTripleDES();
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
des.Key = md5.ComputeHash(Encoding.UTF8.GetBytes(KEY));
string decoded = HttpUtility.UrlDecode(encoded);
// in the act of url encoding and decoding, plus (valid base64 value) gets replaced with space (invalid base64 value). this reverses that.
decoded = decoded.Replace(" ", "+");
string value = Encoding.UTF8.GetString(Convert.FromBase64String(decoded.Split('-')[1]));
string savedHash = Encoding.UTF8.GetString(Convert.FromBase64String(decoded.Split('-')[0]));
string calculatedHash = Encoding.UTF8.GetString(des.ComputeHash(Encoding.UTF8.GetBytes(value)));
if (savedHash != calculatedHash) return null;
return value;
}

I don't like this approach, but it will work.
Once you know you are where you need to be you can Response.Redirect to the same page and they will be gone.

It preserves Query String and Form Variables (optionally). It doesn’t show the real URL where it redirects the request in the users web browser. Server.Transfer happens without the browser knowing anything. The browser requests a page, but the server returns the content of another.
protected void btnServer_Click(object sender, EventArgs e)
{
Server.Transfer("~/About.aspx?UserId=2");
}

Related

How can I catch a pound sign(#) from URL with multiple values in ASHX files

This is my ASHX File where I am catching multiple parameters from request URL using httpcontext, and it is working properly but when I am including a Hash(#) value in Text parameter through the URL. It is not taking the value of FLOW which is another parameter(next to Text parameter).
So it is working for:
http://localhost:10326/ussd.ashx?user=MSL&pass=MSL663055&tid=65506&msisdn=8801520101525&text=***3333**&flow=begin&momt=mo
And it is not working for:
http://localhost:10326/ussd.ashx?user=MSL&pass=MSL663055&tid=65506&msisdn=8801520101525&text=***3333#**&flow=begin&momt=mo
My ASHX files:
public void ProcessRequest(HttpContext context)
{
HttpRequest httpRequest = context.Request;
string user = httpRequest.QueryString["user"].ToString();
string pass = httpRequest.QueryString["pass"].ToString();
string tid = httpRequest.QueryString["tid"].ToString();
string msisdn = httpRequest.QueryString["msisdn"].ToString();
string text = httpRequest.QueryString["text"].ToString();
flow = httpRequest.QueryString["flow"].ToString();
HttpContext.Current.Session["user"] = user;
HttpContext.Current.Session["pass"] = pass;
HttpContext.Current.Session["tid"] = tid;
HttpContext.Current.Session["msisdn"] = msisdn;
HttpContext.Current.Session["text"] = text;
HttpContext.Current.Session["flow"] = flow;
You need to URI encode your parameter values before they are added to the URL. This way the server will not get confused by unsafe characters such as '#' which has its own meaning when included as part of a URL. See RFC 3986 Section 2.
See Encode URL in JavaScript as an example of how to encode the sent data using JavaScript. Whatever is sending the data in the URL will need to do the encoding. There is not much you can do once the request has reached the server. Without knowing how your URL is being created, I can't offer much more.
In short: the problem is with your client code not your ASHX file.

Http.Page.Request[" "] not returning correct values

Situation:-
There is a Home.aspx page, which can be opened by a unique user ("userName" variable).
This page has a popup window control name 'alertWindow'.
In the pageLoad event of Home.aspx.cs, Welcome.aspx page is opened in the 'alertWindow' using NavigateUrl property.
The querystring passed to Welcome.aspx page contains a parameter "UserName" and this parameter is set to the logged in user's name ("userName" variable).
Now when the code execution comes to Welcome.aspx.cs page, "Request["UserName"]" is used to get\retrieve the current "userName" paramerter existing in the query string.
Issue:-
When a logged-in user's name has space or other non-usual characters, then "Request["UserName"].ToString()" doesn't retrieve the actual and correct value.
For Ex. if the logged in "userName" = "A&T Telecom", then "Request["UserName"].ToString() retrieves only "A" and nothing else.
But if the userName string is a proper value like "micheal", then "Request["UserName"].ToString() retrieves only "Micheal" correctly
Requirement:-
Please provide a way so that I get the correct value from Request["UserName"] for any kind of "userName" string value.
Home.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
if (user is valid)
alertWindow.NavigateUrl = "Welcome.aspx?userName=" + currentUser.ToString();
}
Welcome.aspx.cs:-
currentUserName = Request["userName"].ToString();
This is logical because you do not Encode your url. Try this:
alertWindow.NavigateUrl = "Welcome.aspx?userName=" + Server.UrlEncode(currentUser.ToString());
To say few more, they are some special characters that used on the URL, like the
: / # ? & # % + (and the space).
All that characters must be encode to a different format, so the url will not break, the UrlEncode do exactly that.
Two notes.
I select the Server to call the UrlEncode because is not depend from the Request, and you can use it inside a thread, or any function that is not called from the Page.
The Request.QueryString make UrlDecode when you use it. To get the encode url you call the Request.RawUrl
You cannot add white spaces within your url, it needs encoding so :
//uses HttpUtility.UrlEncode internally
Server.UrlEncode("something with spaces");
or
HttpUtility.UrlEncode("something with spaces");

How do I get Url.Action to use the right port number?

I'm creating a website using MVC3, I'm using the razor syntax to create the views and it's all running under azure.
Currently I'm running under the azure emulator locally.
I have a view at the url: 'http://localhost:81/Blah/Foo'.
In that view I want to get the Url for another action.
To achieve this I use: Url.Action("SomeAction", "SomeController", null, this.Request.Url.Scheme)
However because of the load balancing the azure emulator does the port number the request is made on changes.
i.e. whilst it's running on port 81, the request might come from port 82.
This leads to to create an incorrect url 'http://localhost:82/Blah/Bar' and I get a 400, bad hostname error.
Following the info in this post http://social.msdn.microsoft.com/Forums/en-US/windowsazure/thread/9142db8d-0f85-47a2-91f7-418bb5a0c675/ I found that I could get the correct host and port number using HttpContext.Request.Headers["Host"].
But I can only pass a host-name to Url.Action, if I try passing the host-name and port then it still appends what it thinks is the right port so I end up with localhost:81:82.
EDIT: I found someone with the same problem. They seem to have gathered the same information I have (except they've included a reproduction too) but they don't have a useful fix as I can't specify the port number manually.
http://social.msdn.microsoft.com/Forums/en-US/windowsazuredevelopment/thread/87c729e8-094c-4578-b9d1-9c8ff7311577/
I suppose one fix would be to make my own Url.Action overload that lets me specify the port.
For everyone coming here who actually NEEDS an absolute path and are behind a load balanced system, here's what I came up with:
//http://stackoverflow.com/questions/126242/how-do-i-turn-a-relative-url-into-a-full-url
public static string AbsoluteAction(this UrlHelper url, string actionName, string controllerName, object routeValues = null)
{
Uri publicFacingUrl = GetPublicFacingUrl(url.RequestContext.HttpContext.Request, url.RequestContext.HttpContext.Request.ServerVariables);
string relAction = url.Action(actionName, controllerName, routeValues);
//this will always have a / in front of it.
var newPort = publicFacingUrl.Port == 80 || publicFacingUrl.Port == 443 ? "" : ":"+publicFacingUrl.Port.ToString();
return publicFacingUrl.Scheme + Uri.SchemeDelimiter + publicFacingUrl.Host + newPort + relAction;
}
And then, from https://github.com/aarnott/dotnetopenid/blob/v3.4/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs via http://go4answers.webhost4life.com/Example/azure-messing-port-numbers-creates-28516.aspx
/// <summary>
/// Gets the public facing URL for the given incoming HTTP request.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="serverVariables">The server variables to consider part of the request.</param>
/// <returns>
/// The URI that the outside world used to create this request.
/// </returns>
/// <remarks>
/// Although the <paramref name="serverVariables"/> value can be obtained from
/// <see cref="HttpRequest.ServerVariables"/>, it's useful to be able to pass them
/// in so we can simulate injected values from our unit tests since the actual property
/// is a read-only kind of <see cref="NameValueCollection"/>.
/// </remarks>
internal static Uri GetPublicFacingUrl(HttpRequestBase request, NameValueCollection serverVariables)
{
//Contract.Requires<ArgumentNullException>(request != null);
//Contract.Requires<ArgumentNullException>(serverVariables != null);
// Due to URL rewriting, cloud computing (i.e. Azure)
// and web farms, etc., we have to be VERY careful about what
// we consider the incoming URL. We want to see the URL as it would
// appear on the public-facing side of the hosting web site.
// HttpRequest.Url gives us the internal URL in a cloud environment,
// So we use a variable that (at least from what I can tell) gives us
// the public URL:
if (serverVariables["HTTP_HOST"] != null)
{
//ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols.");
string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ?? request.Url.Scheme;
Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]);
UriBuilder publicRequestUri = new UriBuilder(request.Url);
publicRequestUri.Scheme = scheme;
publicRequestUri.Host = hostAndPort.Host;
publicRequestUri.Port = hostAndPort.Port; // CC missing Uri.Port contract that's on UriBuilder.Port
return publicRequestUri.Uri;
}
// Failover to the method that works for non-web farm enviroments.
// We use Request.Url for the full path to the server, and modify it
// with Request.RawUrl to capture both the cookieless session "directory" if it exists
// and the original path in case URL rewriting is going on. We don't want to be
// fooled by URL rewriting because we're comparing the actual URL with what's in
// the return_to parameter in some cases.
// Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless
// session, but not the URL rewriting problem.
return new Uri(request.Url, request.RawUrl);
}
What happens if you just use Url.Action("Action", "Controller")? That should just generate a relative URL, which should work.
(Or perhaps a better question is: why aren't you using that overload?)
I found this worked for me...
var request = HttpContext.Request;
string url = request.Url.Scheme + "://" +
request.UserHostAddress + ":" +
request.Url.Port;

Create temporary link for download

I use ASP.NET
I need to give user temporary link for downloading file from server.
It should be a temporary link (page), which is available for a short time (12 hours for example). How can I generate this link (or temporary web page with link)?
Here's a reasonably complete example.
First a function to create a short hex string using a secret salt plus an expiry time:
public static string MakeExpiryHash(DateTime expiry)
{
const string salt = "some random bytes";
byte[] bytes = Encoding.UTF8.GetBytes(salt + expiry.ToString("s"));
using (var sha = System.Security.Cryptography.SHA1.Create())
return string.Concat(sha.ComputeHash(bytes).Select(b => b.ToString("x2"))).Substring(8);
}
Then a snippet that generates a link with a one week expiry:
DateTime expires = DateTime.Now + TimeSpan.FromDays(7);
string hash = MakeExpiryHash(expires);
string link = string.Format("http://myhost/Download?exp={0}&k={1}", expires.ToString("s"), hash);
Finally the download page for sending a file if a valid link was given:
DateTime expires = DateTime.Parse(Request.Params["exp"]);
string hash = MakeExpiryHash(expires);
if (Request.Params["k"] == hash)
{
if (expires < DateTime.UtcNow)
{
// Link has expired
}
else
{
string filename = "<Path to file>";
FileInfo fi = new FileInfo(Server.MapPath(filename));
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
Response.AddHeader("Content-Length", fi.Length.ToString());
Response.WriteFile(fi.FullName);
Response.Flush();
}
}
else
{
// Invalid link
}
Which you should certainly wrap in some exception handling to catch mangled requests.
http://example.com/download/document.pdf?token=<token>
The <token> part is key here. If you don't want to involve a database, encrypt link creation time, convert it to URL-safe Base64 representation and give user that URL. When it's requested, decrypt token and compare date stored in there with current date and time.
Alternatively, you can have a separate DownloadTokens table wich will map said tokens (which can be GUIDs) to expiration dates.
Append a timestamp to the URL, in the querystring:
page.aspx?time=2011-06-22T22:12
Check the timestamp against the current time.
To avoid the user changing the timestamp by himself, also compute some secret hash over the timestamp, and also append that to the querystring:
page.aspx?time=2011-06-22T22:12&timehash=4503285032
As hash you can do something like the sum of all fields in the DateTime modulo some prime number, or the SHA1 sum of the string representation of the time. Now the user will not be able to change the timestamp without knowing the correct hash. In your page.aspx, you check the given hash against the hash of the timestamp.
There's a million ways to do it.
The way I did once for a project was to generate a unique key and use a dynamic downloader script to stream the file. when the file request was made the key was generated and stored in db with a creation time and file requested. you build a link to the download script and passed in the key. from there it was easy enough to keep track of expiration.
llya
I'll assume you're not requiring any authentication and security isn't an issue - that is if anyone gets the URL they will also beable to download the file.
Personally I'd create a HttpHandler and then create some unique string that you can append to the URL.
Then within the ProcessRequest void test the encoded param to see if it's still viable (with in your specified time-frame) if so use BinaryWrite to render the File or if not you can render some HTML using Response.Write("Expired")
Something like :
public class TimeHandler : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest ( HttpContext context )
{
if( this.my_check_has_expired( this.Context.Request.Params["my_token"] ) )
{
// Has Expired
context.Response.Write( "URL Has Expired" );
return;
}
// Render the File
Stream stream = new FileStream( File_Name , FileMode.Open );
/* read the bytes from the file */
byte[] aBytes = new byte[(int)oStream.Length];
stream.Read( aBytes, 0, (int)oStream.Length );
stream.Close( );
// Set Headers
context.Response.AddHeader( "Content-Length", aBytes.Length.ToString( ) );
// ContentType needs to be set also you can force Save As if you require
// Send the buffer
context.Response.BinaryWrite( aBytes );
}
}
You need to then setup the Handler in IIS, but that a bit different depending on the version you're using.

ISO-8859-1 to UTF8 in ASP.NET 2

We've got a page which posts data to our ASP.NET app in ISO-8859-1
<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<title>`Sample Search Invoker`</title>
</head>
<body>
<form name="advancedform" method="post" action="SearchResults.aspx">
<input class="field" name="SearchTextBox" type="text" />
<input class="button" name="search" type="submit" value="Search >" />
</form>
and in the code behind (SearchResults.aspx.cs)
System.Collections.Specialized.NameValueCollection postedValues = Request.Form;
String nextKey;
for (int i = 0; i < postedValues.AllKeys.Length; i++)
{
nextKey = postedValues.AllKeys[i];
if (nextKey.Substring(0, 2) != "__")
{
// Get basic search text
if (nextKey.EndsWith(XAEConstants.CONTROL_SearchTextBox))
{
// Get search text value
String sSentSearchText = postedValues[i];
System.Text.Encoding iso88591 = System.Text.Encoding.GetEncoding("iso-8859-1");
System.Text.Encoding utf8 = System.Text.Encoding.UTF8;
byte[] abInput = iso88591.GetBytes(sSentSearchText);
sSentSearchText = utf8.GetString(System.Text.Encoding.Convert(iso88591, utf8, abInput));
this.SearchText = sSentSearchText.Replace('<', ' ').Replace('>',' ');
this.PreviousSearchText.Value = this.SearchText;
}
}
}
When we pass through Merkblätter it gets pulled out of postedValues[i] as Merkbl�tter
The raw string string is Merkbl%ufffdtter
Any ideas?
You have this line of code:-
String sSentSearchText = postedValues[i];
The decoding of octets in the post has happen here.
The problem is that META http-equiv doesn't tell the server about the encoding.
You could just add RequestEncoding="ISO-8859-1" to the #Page directive and stop trying to fiddle around with the decoding yourself (since its already happened).
That doesn't help either. It seems you can only specify the Request encoding in the web.config.
Better would be to stop using ISO-8859-1 altogether and leave it with the default UTF-8 encoding. I can see no gain and only pain with using a restrictive encoding.
Edit
If it seems that changing the posting forms encoding is not a possibility then we seem to be left with no alternative than to handle the decoding ourselves. To that end include these two static methods in your receiving code-behind:-
private static NameValueCollection GetEncodedForm(System.IO.Stream stream, Encoding encoding)
{
System.IO.StreamReader reader = new System.IO.StreamReader(stream, Encoding.ASCII);
return GetEncodedForm(reader.ReadToEnd(), encoding);
}
private static NameValueCollection GetEncodedForm(string urlEncoded, Encoding encoding)
{
NameValueCollection form = new NameValueCollection();
string[] pairs = urlEncoded.Split("&".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (string pair in pairs)
{
string[] pairItems = pair.Split("=".ToCharArray(), 2, StringSplitOptions.RemoveEmptyEntries);
string name = HttpUtility.UrlDecode(pairItems[0], encoding);
string value = (pairItems.Length > 1) ? HttpUtility.UrlDecode(pairItems[1], encoding) : null;
form.Add(name, value);
}
return form;
}
Now instead of assigning:-
postedValues = Request.Form;
use:-
postValues = GetEncodedForm(Request.InputStream, Encoding.GetEncoding("ISO-8859-1"));
You can now remove the encoding marlarky from the rest of the code.
I think adding your encoding into web.config like that will probably solve your problem :
<configuration>
<system.web>
<globalization
fileEncoding="iso-8859-1"
requestEncoding="iso-8859-1"
responseEncoding="iso-8859-1"
culture="en-US"
uiCulture="en-US"
/>
</system.web>
</configuration>
We had the same problem that you have. The topic is not straight-forward at all.
The first tip is to set the Response encoding of the page that posts the data (usually the same page as the one that receives the data in .NET) to the desired form post encoding.
However, this is just a hint to the user's browser on how to interpret the characters sent from the server. The user might choose to override the encoding manually. And, if the user overrides the encoding of the page, the encoding of the data sent in the form is also changed (to whatever the user has set the encoding to).
There is a small trick, though. If you add a hidden field with the name _charset_ (notice the underscores) in your form, most browsers will fill out this form field with the name of the charset used when posting the form. This form field is also a part of the HTML5 specification.
So, you might think your're good to go, however, when in your page, ASP.NET has already urldecoded all parameters sent in to the form. So when you actually have the value in the _charset_ field, the value of the field containing Merkblätter is already decoded incorrectly by .NET.
You have two options:
In the ASP.NET page in question, perform the parsing of the request string manually
In Application_BeginRequest, in Global.asax, parse the request parameters manually, extracting the _charset_field. When you get the value, set Request.ContentEncoding to System.Text.Encoding.GetEncoding(<value of _charset_ field>). If you do this, you can read the value of the field containing Merkblätter as usual, no matter what charset the client sends the value in.
In either of the cases above, you need to manually read Request.InputStream, to fetch the form data. I would recommend setting the Response Encoding to UTF-8 to have the greatest number of options in which characters you accept, and then treating the special cases when the user has overridden the charset especially, as specified above.
Function urlDecode(input)
inp = Replace(input,"/","%2F")
set conn = Server.CreateObject("MSXML2.ServerXMLHTTP")
conn.setOption(2) = SXH_SERVER_CERT_IGNORE_ALL_SERVER_ERRORS
conn.open "GET", "http://www.neoturk.net/urldecode.asp?url=" & inp, False
conn.send ""
urlDecode = conn.ResponseText
End Function
To speed this up, just create a table on your db for decoded and encoded urls and read them on global.asa application.on_start section. Later put them on the application object.
Then put a check procedure for that application obj. in above function and IF decoded url not exists on app array, THEN request it one time from remote page (tip: urldecode.asp should be on different server see: http://support.microsoft.com/default.aspx?scid=kb;en-us;Q316451) and insert it to your db and append to application array object, ELSE return the function from the application obj.
This is the best method I have ever found.
If anybody wants further details on application object, database operations etc. contact me via admin#neoturk.net
You can see above method successfully working at: lastiktestleri.com/Home
I also used, HeliconTech's ISAPI_Rewrite Lite version
usage is simple: url = Request.ServerVariables("HTTP_X_REWRITE_URL")
this will return the exact url directed to /404.asp
That's because you are encoding the string as ISO-8859-1 and decoding it as if it was a string encoded as UTF-8. This will surely mess up the data.
The form isn't posting the data as ISO-8859-1 just because you send the page using that encoding. You haven't specified any encoding for the form data, so the browser will choose an encoding that is capable of handling the data in the form. It may choose ISO-8859-1, but it may just as well choose some other encoding.
The data is send to the server, where it's decoded and put in the Request.Form collection, according to the encoding that the browser specifies.
All you have to do is to read the string that has already been decoded from the Request.Form collection. You don't have to loop through all the items in the collection either, as you already know the name of the text box.
Just do:
string sentSearchText = Request.Form("SearchTextBox");
What I ended up doing was forcing our app to be in ISO-8859-1. Unfortunately the underlying data may contain characters which don't fit nicely into that codepage so we go through the data before displaying it and convert everything about the character code of 127 into an entity. Not ideal but works for us...
I had the same problem, solved like this:
System.Text.Encoding iso_8859_2 = System.Text.Encoding.GetEncoding("ISO-8859-2");
System.Text.Encoding utf_8 = System.Text.Encoding.UTF8;
NameValueCollection n = HttpUtility.ParseQueryString("RT=A+v%E1s%E1rl%F3+nem+enged%E9lyezte+a+tranzakci%F3t", iso_8859_2);
Response.Write(n["RT"]);
A+v%E1s%E1rl%F3+nem+enged%E9lyezte+a+tranzakci%F3t will return "A vásárló nem engedélyezte a tranzakciót" as expected.

Resources