GETting a URL with an url-encoded slash
Asked Answered
D

5

40

I want to send a HTTP GET to http://example.com/%2F. My first guess would be something like this:

using (WebClient webClient = new WebClient())
{
  webClient.DownloadData("http://example.com/%2F");
}

Unfortunately, I can see that what is actually sent on the wire is:

GET // HTTP/1.1
Host: example.com
Connection: Keep-Alive

So http://example.com/%2F gets translated into http://example.com// before transmitting it.

Is there a way to actually send this GET-request?

The OCSP-protocol mandates sending the url-encoding of a base-64-encoding when using OCSP over HTTP/GET, so it is necessary to send an actual %2F rather than an '/' to be compliant.

EDIT:

Here is the relevant part of the OCSP protocol standard (RFC 2560 Appendix A.1.1):

An OCSP request using the GET method is constructed as follows:

GET {url}/{url-encoding of base-64 encoding of the DER encoding of the OCSPRequest}

I am very open to other readings of this, but I cannot see what else could be meant.

Degrading answered 23/4, 2009 at 10:51 Comment(6)
Sounds like a bug in the OCSP protocol to me (or, alternatively, a misinterpretation of it).Cottbus
I recommend reporting the bug to MicrosoftKeef
@knocte: It has been reported and fixed. See Bradley Gaingers answer.Degrading
oh, right, sorry for the noiseKeef
actually, either the link is broken, or the bug report is private :(Keef
https://mcmap.net/q/297973/-accessing-private-members-from-powershell — for similar problem in Power ShellAcarpous
N
31

By default, the Uri class will not allow an escaped / character (%2f) in a URI (even though this appears to be legal in my reading of RFC 3986).

Uri uri = new Uri("http://example.com/%2F");
Console.WriteLine(uri.AbsoluteUri); // prints: http://example.com//

(Note: don't use Uri.ToString to print URIs.)

According to the bug report for this issue on Microsoft Connect, this behaviour is by design, but you can work around it by adding the following to your app.config or web.config file:

<uri>
  <schemeSettings>
    <add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
  </schemeSettings>
</uri>

(Reposted from https://stackoverflow.com/a/10415482 because this is the "official" way to avoid this bug without using reflection to modify private fields.)

Edit: The Connect bug report is no longer visible, but the documentation for <schemeSettings> recommends this approach to allow escaped / characters in URIs. Note (as per that article) that there may be security implications for components that don't handle escaped slashes correctly.

Nga answered 29/8, 2012 at 2:32 Comment(1)
Be sure to use name="https" if the URL you are calling is HTTPS.Frogfish
D
53

This is a terrible hack, bound to be incompatible with future versions of the framework and so on.

But it works!

(on my machine...)

Uri uri = new Uri("http://example.com/%2F");
ForceCanonicalPathAndQuery(uri);
using (WebClient webClient = new WebClient())
{
  webClient.DownloadData(uri);
}

void ForceCanonicalPathAndQuery(Uri uri){
  string paq = uri.PathAndQuery; // need to access PathAndQuery
  FieldInfo flagsFieldInfo = typeof(Uri).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
  ulong flags = (ulong) flagsFieldInfo.GetValue(uri);
  flags &= ~((ulong) 0x30); // Flags.PathNotCanonical|Flags.QueryNotCanonical
  flagsFieldInfo.SetValue(uri, flags);
}
Degrading answered 24/4, 2009 at 8:15 Comment(10)
+1. Perfect! I needed this to get the Google Webmaster Tools API to work.Klausenburg
works indeed. I wonder what the reason is for them not allowing the 'dontEscape' parameter?Elberfeld
For the love of Atwood this answer saved me hours. Thanks!Etui
Why for the love of baby jesus isn't this the default behavior of WebClient? Thank you for this solution Rasmus.Selfabnegation
The above solution does not work in a shared server environment with medium trust. Is there any other way to do this without reflection? - Jerry HogsettKistler
@user1473484: Yes, you can work around this by changing your app.config; see my answer to a similar question here: stackoverflow.com/a/10415482Nga
Rasmus, I just went and created a library for .NET and Mono to address this. I used your approach for the .NET version. It's here: github.com/glennblock/PUrify/blob/master/README.md. I've added your name in the credits, if you have concerns let me know.Drabeck
Thanks for this. Best solution to this problem that I've found.Aborning
This is great! But do you have a solution for preventing "%2B" escaping into "+"? I want tried this but it doesn't work for "%2B"...Clareta
To get this to work under .Net Standard (and possibly recent versions of .Net Framework) you will need to add flags &= ~((ulong)0xC30); and change m_Flags to _flagsSuperheterodyne
N
31

By default, the Uri class will not allow an escaped / character (%2f) in a URI (even though this appears to be legal in my reading of RFC 3986).

Uri uri = new Uri("http://example.com/%2F");
Console.WriteLine(uri.AbsoluteUri); // prints: http://example.com//

(Note: don't use Uri.ToString to print URIs.)

According to the bug report for this issue on Microsoft Connect, this behaviour is by design, but you can work around it by adding the following to your app.config or web.config file:

<uri>
  <schemeSettings>
    <add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
  </schemeSettings>
</uri>

(Reposted from https://stackoverflow.com/a/10415482 because this is the "official" way to avoid this bug without using reflection to modify private fields.)

Edit: The Connect bug report is no longer visible, but the documentation for <schemeSettings> recommends this approach to allow escaped / characters in URIs. Note (as per that article) that there may be security implications for components that don't handle escaped slashes correctly.

Nga answered 29/8, 2012 at 2:32 Comment(1)
Be sure to use name="https" if the URL you are calling is HTTPS.Frogfish
D
13

Update on this: It looks like the default behavior of the Uri class was actually changed in .NET 4.5, and you can now use escaped slashes and they will not be touched.

I ran the following code in .NET 3.5, .NET 4.0, .NET 4.5/4.5.1

static void Main(string[] args)
{
    var uri = new Uri("http://www.yahooo.com/%2F");
    var client = new WebClient();
    client.DownloadString(uri);
}

In .NET 3.5/4.0 the trace shows that the %2F was in fact unescaped as expected.

Fiddler trace

However, In .NET 4.5/4.5.1 you can see the %2F was not unescaped (notice the GET /%2F)

Fiddler trace

You can even use ToString() now on the Uri and you'll get the same result.

So in conclusion, it appears if you are using .NET >= .NET 4.5 then things will behave as they should inline with the RFC.

I just did an exploration of trying to get the same approach working on Mono. I posted my question on the approach here: Getting a Uri with escaped slashes on mono

Drabeck answered 22/12, 2013 at 20:15 Comment(1)
I would like to add, that the change only takes effect when changing the "Target Framework" in Visual Studio to .NET 4.5. Just having .NET 4.5 installed on the machine will not change the behaviour.Swore
S
0

As mentioned in my comment on the answer posted by Ramus, the following is required for .Net Standard (and possibly later versions of .Net Framework) to get this hack working:

Uri uri = new Uri("http://example.com/%2F");
ForceCanonicalPathAndQuery(uri);
using (WebClient webClient = new WebClient())
{
  webClient.DownloadData(uri);
}

void ForceCanonicalPathAndQuery(Uri uri){
  string paq = uri.PathAndQuery; // need to access PathAndQuery
  FieldInfo flagsFieldInfo = typeof(Uri).GetField("_flags", BindingFlags.Instance | BindingFlags.NonPublic);
  ulong flags = (ulong) flagsFieldInfo.GetValue(uri);
  flags &= ~((ulong) 0xC30); // Flags.PathNotCanonical|Flags.QueryNotCanonical
  flagsFieldInfo.SetValue(uri, flags);
}
Superheterodyne answered 13/10, 2019 at 5:18 Comment(0)
G
-7

Double encode it : %252F

But also if you use HttpWebRequest you can actually tell not to encode the URL, either way it should work.

Also If WebClient accepts URI then you can create a new URI and you can set it to not encode.

Geri answered 23/4, 2009 at 10:55 Comment(2)
If I try to get example.com/%252F, it actually sends GET /%252F, so that does not work. The URI-constructor with dontEscape is deprecated since 2.0 and according to the documentation, the dontEscape-parameter is ignored. Was that what you meant about using HttpWebRequest?Degrading
Check if the problem is in URI constructor? or the actual sending process? this can help to diagnose to exact problem.Geri

© 2022 - 2024 — McMap. All rights reserved.