Is IIS performing an illegal character substitution? If so, how to stop it? - asp.net

Context: ASP.NET MVC running in IIS, with a a UTF-8 %-encoded URL.
Using the standard project template, and a test-action in HomeController like:
public ActionResult Test(string id)
{
return Content(id, "text/plain");
}
This works fine for most %-encoded UTF-8 routes, such as:
http://mydevserver/Home/Test/%e4%ba%ac%e9%83%bd%e5%bc%81
with the expected result 京都弁
However using the route:
http://mydevserver/Home/Test/%ee%93%bb
the url is not received correctly.
Aside: %ee%93%bb is %-encoded code-point 0xE4FB; basic-multilingual-plane, private-use area; but ultimately - a valid unicode code-point; you can verify this manually, or via:
string value = ((char) 0xE4FB).ToString();
string encoded = HttpUtility.UrlEncode(value); // %ee%93%bb
Now, what happens next depends on the web-server; on the Visual Studio Development Server (aka cassini), the correct id is received - a string of length one, containing code-point 0xE4FB.
If, however, I do this in IIS or IIS Express, I get a different id, specifically "î“»", code-points: 0xEE, 0x201C, 0xBB. You will immediately recognise the first and last as the start and end of our percent-encoded string... so what happened in the middle?
Well:
code-point 0x93 is “ (source)
code-point 0x201c is “ (source)
It looks to me very much like IIS has performed some kind of quote-translation when processing my url. Now maybe this might have uses in a few scenarios (I don't know), but it is certainly a bad thing when it happens in the middle of a %-encoded UTF-8 block.
Note that HttpContext.Current.Request.Raw also shows this translation has occurred, so this does not look like an MVC bug; note also Darin's comment, highlighting that it works differently in the path vs query portion of the url.
So (two-parter):
is my analysis missing some important subtlety of unicode / url processing?
how do I fix it? (i.e. make it so that I receive the expected character)

id = Encoding.UTF8.GetString(Encoding.Default.GetBytes(id));
This will give you your original id.
IIS uses Default (ANSI) encoding for path characters. Your url encoded string is decoded using that and that is why you're getting a weird thing back.
To get the original id you can convert it back to bytes and get the string using utf8 encoding.
See Unicode and ISAPI Filters
ISAPI Filter is an ANSI API - all values you can get/set using the API
must be ANSI. Yes, I know this is shocking; after all, it is 2006 and
everything nowadays are in Unicode... but remember that this API
originated more than a decade ago when barely anything was 32bit, much
less Unicode. Also, remember that the HTTP protocol which ISAPI
directly manipulates is in ANSI and not Unicode.
EDIT: Since you mentioned that it works with most other characters so I'm assuming that IIS has some sort of encoding detection mechanism which is failing in this case. As a workaround though you can prefix your id with this char and then you can easily detect if the problem occurred (if this char is missing). Not a very ideal solution but it will work. You can then write your custom model binder and a wrapper class in ASP.NET MVC to make your consumption code cleaner.

Once Upon A Time, URLs themselves were not in UTF-8. They were in the ANSI code page. This facilitates the fact that they often are used to select, well, pathnames in the server's file system. In ancient times, IE had an option to tell whether you wanted to send UTF-8 URLs or not.
Perhaps buried in the bowels of the IIS config there is a place to specify the URL encoding, and perhaps not.

Ultimately, to get around this, I had to use request.ServerVariables["HTTP_URL"] and some manual parsing, with a bunch of error-handling fallbacks (additionally compensating for some related glitches in Uri). Not great, but only affects a tiny minority of awkward requests.

Related

Why is ASP.NET 4 / IIS7 html-encoding my query string?

We've switched one of our test environments to using .NET 4 on IIS7. Production is using .NET 2.
Certain urls, such as
http://www.example.com/page.aspx?param1=<foo>&param2=<foo>
Aren't getting caught by our stringindex code that looks for < or > in Request.Url.ToString(). Why? Because they're showing up as <foo> when we check. This worked in .NET 2.
What is going on?
NOTE: there are no mistakes in the formatting. I really mean HTML encode.
All data in query string needs to be URL Encoded to be able to parse correctly, so if you want to grab what you entered you need to URL Decode the query string.
HttpServerUtility.UrlDecode(Request.QueryString);
http://msdn.microsoft.com/en-us/library/6196h3wt.aspx :
URL encoding ensures that all browsers will correctly transmit text in URL strings. Characters such as a question mark (?), ampersand (&), slash mark (/), and spaces might be truncated or corrupted by some browsers. As a result, these characters must be encoded in tags or in query strings where the strings can be re-sent by a browser in a request string.

Server.Encode / decode not working in production server

I pass a vb.net query string to a page with, depending on the parameter, a spanish character on it, wich works perfectly on the development and testing servers, but not in production.
So, inside the query string I encode the name like this:
Server.UrlEncode(name)
And even before it gets to load, the server throws a "500 Internal Server Error"
with this url:
http://website/Dir/Page.aspx?num=CEJMEJINMFCEGICCEH&name=Jose+Bonse%c3%b1or+Del+Rosario
when I replace the %c3%b1 for any other normal letter, it works (%c3%b1 is an "ñ")
Again, its works just fine on the other servers, except on production, I dont even know where to start looking..
The HttpServerUtility class (which "Server" is an instance of in the Page base class) uses ASCII by default to decode and encode URL parameters. ASCII, of course, is not vast enough to handle other localizations. To force Unicode, try using UrlEncodeUnicode function of the HttpUtility class instead...
HttpUtility.UrlEncodeUnicode(name)
There are two things that pop to mind although I cannot say that I've actually tested either in this specific case (request string encoding).
First, how is your page encoded? That is, do you indicate the character sets used?
Second, how is the server computer configured re: localization? I'd compare the "Regional and Language Options" in the Control Panel on the three computers.
Finally (Ok, that's three) there are localization options in Web.Config. As is often true, CodeProject has a great article on the subject.
Good luck!

How may i escape / and other characters in asp.net?

When i visit http://localhost:17357/u/a%2fa/m/ssd-10 and look at HttpContext.Current.Request.Url in Application_BeginRequest i see http://localhost:17357/u/a/a/m/ssd-10 huh? shouldnt i get http://localhost:17357/u/a%2fa/m/ssd-10? i thought the point of escaping urls is so ?, &, / and other special symbols not be confused with their special meaning in urls. Maybe theres a config i need to tweak?
I created 4 usernames, there are
a?#!&ee
a?#!/&ee
as d
クイン
with the links as
a?#!&ee<br>
a?#!/&ee<br>
as d<br>
クイン
The last two work, but the first two i get the exceptio
A first chance exception of type 'System.ArgumentException' occurred in mscorlib.dll
Additional information: Illegal characters in path.
then
A first chance exception of type 'System.Web.HttpException' occurred in System.Web.dll
Additional information: '/u/a?#!&ee' is not a valid virtual path.
and my page says Bad Request. How can i allow these usernames to work. If its impossible how can i write a workaround?
You need to escape it again. Use %252f instead of %2f. To clarify, the URL is unencoded when the server receives it. URL encoding allows you to pass in a / that the server processes as a character instead of the special function that a reserved character would normally trigger. See the Wikipedia page for more info.
Concerning your error with the a?#!&ee username, it seems almost certain that you're running into a problem that ASP.NET has with special characters (even urlencoded properly) that are not in the query string (that is, after the ? part of the URL). Joshua Flanagan talks about it in a blog post, and identifies %, &, *, and : as the problematic characters.
He points to a Dirk.Net blog post that offers a couple of fixes. First, you can edit the registry to allow restricted characters (adding a DWORD key AllowRestrictedChars to HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters and setting its boolean value to true). Or, you can ensure that you have the .NET framework 1.1 SP1 and edit the registry to set ASP.NET VErification Compatibility to true (DWORD HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET VerificationCompatibility = 1). Third, you can try setting ValidateRequest to false on the ASPX page. Finally, as Joshua decided to do, you can pass the information using the query string, i.e. after the ? as ASP.Net originally (pre MVC) expected.
I wrote my own solution. Its nice to have a username with / but not consider as / when getting a GET request.

when assigning location.href, please explain url encoding (in asp.net and firefox)

In some javascript, I have:
var url = "find.aspx?" + "location=" + encodeURIComponent( address );
alert( url );
location.href = url;
where the value of address is the string "Seattle, WA".
In the alert I see
find.aspx?Seattle%2C%20WA
as I expect.
But on the server side, when I look at Request.Url, the relevant substring I see is
find.aspx?Seattle, WA
And in the Firefox url window I see
find.aspx?location=Seattle%2C WA
So I'm getting three different representations whereas I would expect that in all three places I should see what I see in the alert. My expectation is that the url I assign to location.href should show up as-is in the browser url window, and should be passed as-is to the server in Request.Url (and I would need to decode the values on the server before using them). What's happening?
Firefox converts certain encoded characters into their literal forms as a way to be friendly to users. It will also convert spaces typed into the address bar into %20 for the server.
Update: The reason Firefox doesn't display the comma unencoded is because commas are allowed in URLs, but spaces are not, so it knows that a space is going to be unambiguously interpreted, whereas the pre-encoded comma is different from a non-encoded comma to some servers. see: Can I use commas in a URL?
ASP is probably trying to help you out by auto-un-encoding the string for you.
Update: It looks like ASP.NET unencodes Request.Url for you by default, as mentioned here: QueryString malformed after URLDecode They also mention that you can use HttpRequest.Url.Query to access the un-decoded version.
The alert is the only thing not doing any "magic" for you.
For the alert, you are doing the encoding yourself. Perhaps it looks the same as on the server-side if you removed encodeURIComponent.
On the server side, ASP.NET will always show you the unencoded form. This is to make it easier to directly map to files that also have text that needed to be (un)encoded.
Note that you can replace every letter for its UTF8 representation in URL Encoding. It will still be the same URL. I.e., type the following in the browser window and it will still work: %66%59%6E%64.aspx?location=Seattle%2C%20WA. To only encode the necessary chars, use UrlEncode on the server side if you create a link yourself.
URL encoding can become fairly tricky. You ask to explain it. To know the correct escape of a certain character, you need to know how that character looks in UTF8. The hexadecimal value of the UTF-8 bytes then become the %XX%YY value of your letter. Sometimes it's one %XX, but it can be up to six byte sequences in total (some Chinese characters for instance).
URL Encoding works one way only. Never double-encode or double-unencode. This is prohibited by the specification. Also, because you can encode any character, it is not always possible (as you found out) to do roundtrip encoding/unencoding. If you unencode and re-encode again, it is well possible that the resulting string is different, but syntactically the same.
In HTML, URL Encoding is sometimes interspersed with HTML Encoding. I.e., the ampersand is valid in HTML, but not in HTML. find.aspx?city=A&name=B becomes find.aspx?city=A&name=B in and HTML URL. However, browsers are lenient and will accept wrongly HTML-encoded strings.
Finally, a not on the browser: if you type in a space in a link, even inside an <a> tag, it will escape the space (or other character) for you. Likewise, it will nowadays show the odd characters (é, ï etc) in the address bar, but when it sends it over HTTP, the browser will correctly do the encoding for you.
Update: about anwering your question of needing a "definitive" reference or proof.
While I couldn't find any on the internet, I decided to look for it myself using Reflector. Going through the methods that set, for instance, the HttpRequest.QueryString, you quickly encounter the private method HttpRequest.FillInQueryStringCollection which then calls HttpValueCollection.FillfromEncodedBytes. Somewhat near the end of that method, HttpUtility.UrlDecode is called for the values. Conclusion: do not call it yourself, to prevent double decoding.
You can see this for yourself when you download Reflector and disassemble the .NET libs of System.Web.
For your example you can change this line
var url = "find.aspx?" + "location=" + encodeURIComponent( address );
to
var url = "find.aspx?" + "location=" + address;
and see the address as it is. Bu if address variable contains any '&' character your variable will be corrupt. So you are using encodeURIComponent to encode these things url.
On the Server side all these encoded strings are decoded back. It means encodeURIComponent is just for sending the address variable (whether it contains & character or not) to server side correctly.

Ampersands in URLRewriter Query Strings

I have a query string parameter value that contains an ampersand. For example, a valid value for the parameter may be:
a & b
When I generate the URL that contains the parameter, I'm using System.Web.HTTPUtility.UrlEncode() to make each element URL-friendly. It's (correctly) giving me a URL like:
http://example.com/foo?bar=a+%26b
The problem is that ASP.NET's Request object is interpreting the (encoded) ampersand as a Query String parameter delimiter, and is thus splitting my value into 2 parts (the first has "bar" as the parameter name; the second has a null name).
It appears that ASP.NET is URL-decoding the URL first and then using that when parsing the query string.
What's the best way to work around this?
UPDATE: The problem hinges on URLRewriter (a third-party plugin) and not ASP.NET itself. I've changed the title to reflect this, but I'll leave the rest of the question text as-is until I find out more about the problem.
man,
i am with you in the same boat, i have spent like hours and hours trying to figure out what is the problem, and as you said it is a bug in both, as normal links that contain weird characters or UTF-8 code characters are parsed fine by asp.net.
i think we have to switch to MVC.routing
Update: man you wont believe it, i have found the problem it is so strange, it is with IIS,
try to launch your page from visual studio Dev server and Unicode characters will be parsed just fine, but if you launch the page from IIS 7 it will give you the ???? characters.
hope some body will shade some light here
I would have thought that %26 and '&' mean exactly the same thing to the web server, so its the expected behavior. Urlencode is for encoding URLs, not encoding query strings.
... hang on ...
Try searching for abc&def in google, you'll get:
http://www.google.com.au/search?q=abc%26def
So your query string is correct, %26 is a literal ampersand. Hmm you're right, sounds like a bug. How do you go with an & instead of the %26 ?
Interesting reading:
http://www.stylusstudio.com/xsllist/200104/post11060.html
Switching to UrlRewritingNet.UrlRewrite did not help, as it apparently has the same bug. I'm thinking it might have something to do with ASP.NET after all.
I think URLRewriter has a problem with nameless parameters (null name).
I had a similar problem. When I gave my nameless parameter a (dummy) name, everything worked as expected.

Resources