Look at this code:
public static void Main()
{
try
{
WebClient client = new WebClient();
byte[] data = client.DownloadData("http://website../../../page/1/2021-02/28/01/2021022801_brief.jpg");
Console.WriteLine("data: " + data.Length);
}
catch(Exception exc)
{
Console.WriteLine(exc);
}
}
Run it on .NET fiddle (for example), and you'll get this exception:
System.Net.WebException: Could not find a part of the path 'C:\Resources\directory\2849ebba6307418392168ca985d98463.DotNetFiddle.Web.LocalResources\Sandbox\67be885b-8fe4-40d0-b80e-cc2cfcbebfbe\page\1\2021-02\28\01\2021022801_brief.jpg'. ---> System.Net.WebException: Could not find a part of the path 'C:\Resources\directory\2849ebba6307418392168ca985d98463.DotNetFiddle.Web.LocalResources\Sandbox\67be885b-8fe4-40d0-b80e-cc2cfcbebfbe\page\1\2021-02\28\01\2021022801_brief.jpg'. ---> System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Resources\directory\2849ebba6307418392168ca985d98463.DotNetFiddle.Web.LocalResources\Sandbox\67be885b-8fe4-40d0-b80e-cc2cfcbebfbe\page\1\2021-02\28\01\2021022801_brief.jpg'.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy)
at System.Net.FileWebStream..ctor(FileWebRequest request, String path, FileMode mode, FileAccess access, FileShare sharing, Int32 length, Boolean async)
at System.Net.FileWebResponse..ctor(FileWebRequest request, Uri uri, FileAccess access, Boolean asyncHint)
--- End of inner exception stack trace ---
at System.Net.FileWebResponse..ctor(FileWebRequest request, Uri uri, FileAccess access, Boolean asyncHint)
at System.Net.FileWebRequest.GetResponseCallback(Object state)
--- End of inner exception stack trace ---
at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request)
at System.Net.WebClient.DownloadData(Uri address)
at System.Net.WebClient.DownloadData(String address)
at Program.Main() in d:\Windows\Temp\vbu5djxg.0.cs:line 13
You see the local server path?! how is that possible? The URI begins with http, why .NET tries to find a file locally?
Besides, if the URLs are entered by users, they can access server folder information... isn't that a security flaw?
http://website../../../page/1/2021-02/28/01/2021022801_brief.jpg
is not a valid URI. The overload of DownloadData that takes a string eventually calls a helper that has this bit of code to turn the string into a URI:
if (!Uri.TryCreate(path, UriKind.Absolute, out uri))
return new Uri(Path.GetFullPath(path));
The call to Uri.TryCreate
will fail because the FQDN of website..
isn't valid. Since it fails, it falls back to Path.GetFullPath
From there you're going down a deep tunnel of backwards compatibility work and assumptions that treat your string as a /
delimited string and a valid path and append it to the current working directory. It's then bubbled up as a file://
URI, and when the local file can't be opened, it fails with the error you've seen.
If you're in a scenario where you're accepting user input for a URI, I'd recommend you use Uri.TryCreate
with UriKind.Absolute
yourself to build the URI, and verify the type is one of the types you support (generally just HTTP and HTTPS). Anything else you should treat as an error and bypass some of the assistive layers.