Safe Process.Start implementation for untrusted URL strings - c#

My goal is to safely open a web page in a users default browser. The URL for this web page is considered "untrusted" (think of it as a link in a document opened with this software, but the document could be from anywhere and the links in it could be malicious)
I want to avoid someone passing "C:\Windows\malicious_code.exe" off as a URL
My current thought is to do something like this:
Uri url = new Uri(urlString, UriKind.Absolute);
if( url.Scheme == Uri.UriSchemeHttp || url.Scheme == Uri.UriSchemeHttps )
{
Process.Start(url.AbsoluteUri);
}
Am I forgetting about anything else that my 'urlString' might contain that makes this dangerous (e.g. a new line character which would allow someone to sneak a second process to be started in after the URL or a possible execution of a relative executable starting with http)?
I'm pretty sure both of those cases are handled by this (as I don't believe Process.Start allows you to start two processes as you would in a BATCH file and this should only allow strings starting with http: or https: and are valid urls)
Is there a better way to do this in C#?

What you want to check is the scheme of the url (i.e. ftp://, http://, file://, etc.) Here is a list of schemes: http://en.wikipedia.org/wiki/URI_scheme#Official_IANA-registered_schemes
To find the scheme of a URL, use:
Uri u = new Uri("C:\\Windows");
String scheme = (u.GetLeftPart(UriPartial.Scheme).ToString());
For me, the above example gives file://. Just check the scheme, using the code above, and reject the ones you want to filter. Also, surround the parsing with a try-catch block and if an exception is caught, reject the URL; it can't be parsed so you shouldn't trust it.
If you want to ultra-paranoid-safe, you could always parse the URL using a URL parser and reconstruct it, validating each part as you go along.

Related

API controller route attribute issue

I have an issue with an api controller attribute routing. The API receives a get request containing a scanned barcode to the route: "api/[controller]/{userId}/{barcodeId}". Where the barcode is like: 0015A765248.
But we now have some new barcodes coming in this format: BBRT058/639.
I've found out through debugging that the extra / in the new barcode is causing the issue as the API controller is now not being accessed.
As I think it is trying to reach controller: "api/[controller]/{userId}/{barcodeId}/{barcodeID}"
Which doesn't exist. Is it possible to modify the API controller route attribute to accept the barcodeId containing the additional /?
Actually modifying the new barcode style in any way code wise is not an option I am told.
I have considered creating a new controller to accept the new barcode: "api/[controller]/{userId}/{barcodeIdpart1}/{barcodeIDpart2}" for example.
But i'm not sure if creating a new controller that is a duplicate of the current one, with the only difference being the routing is a good idea. Also both barcode formats are likely still going to be used in the future.
I don't believe you need to make any changes to your code/controller as such and that it seems to be correct. However, I would recommend you update the client to correctly URL encode the barcode before making the outbound call to your controller. This will ensure that the controller correctly parses the barcode and then processes it further. Additionally, this will also ensure that any other special non ASCII characters are correctly handled in the future.
This solution is not really changing the barcode format but is encoding it just before making the outbound HTTP call to correctly transmit it over the wire. Alternate solutions by hacking the controller will usually result in a non standard brittle solution and is not recommended.
Client calls URL
Current: api/controllername/userId/BBRT058/639
Proposed: api/controllername/userId/BBRT058%2F639
For further reading:
https://en.wikipedia.org/wiki/Percent-encoding
https://www.tutorialspoint.com/html/html_url_encoding.htm
https://www.w3schools.com/tags/ref_urlencode.ASP
I would try to replace "/" with "!" or "!!" for example (you can select any sign or combination you like) - BBRT058!!639 before sending to the controller and replace back with "/" inside of the action.
barcodeId = barcodeId.Replace("!!", "/");
if you can't modify the barcode then try this
Route[("api/[controller]/{userId}/{barcodeId}/{barcodeIdpart2?})"]
public IActionResult GetBarcode(int userId, string barcodeId, string barcodeIdpart2 = null)
{
if (!string.IsNullOrEmpty(barcodeIdpart2)) barcodeId=barcodeId + "/" + barcodeIdpart2;
....
}
It will work for both styles

How to manipulate a url to access a parent directory

I have an asp.net web application that is being hosted on an internal network. In my testing environment of course it gets hosted out on localhost:01010/Views/page.aspx. now whenever I take it live the Url changes to server_name/folder 1/folder 2/views/page.aspx. what I am trying to do is get a new page to open up as server_name/folder 1/folder 2/Uploaded_Images/randomimage.png. Now I Can get the url, but as soon as I do a single ".Remove(url.lastindexof("/")+1)" it returns "server_name/folder 1/folder 2/Views". The I perform my second ".Remove(url.lastindexof("/")+1)"
and the it only returns "server_name/". I am ripping my hair out at this one and am hoping somewhere in the world a .net developer already has this built in. Appreciate all the help.
Also just to specify this is webforms and not mvc. also there is no ajax or page manipulation going on except for a response.write to open the new page.
You don't need the +1, this works:
var url = "server_name/folder 1/folder 2/views/page.aspx";
url = url.Remove(url.LastIndexOf("/"));
url = url.Remove(url.LastIndexOf("/"));
Or you could do it like this:
var parts = url.Split('/');
var newPath = string.Join("/", parts.Take(3));
I assume you are talking about URL's used as links to parts of your site and not physical paths on the file system.
In most cases, you should be able to use methods that construct paths on the fly. For example, in any of your .aspx files (or .aspx.cs files), you can use the ResolveUrl method, like this:
Some link
If there are any places where you need the full URL including the domain (like for email notifications or something like that) then what I have done is keep a static variable accessible to my whole application that gets set the first time Application_BeginRequest runs:
void Application_BeginRequest(object sender, EventArgs e) {
if (SiteRoot == null) {
SiteRoot = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) +
(VirtualPathUtility.ToAbsolute("~") == "/" ? "" : VirtualPathUtility.ToAbsolute("~"));
}
}
That will pull the full URL from the Request details (the URL that the user used to access your site), without any trailing slash.

How to maintain the right URL in C#/ASP.NET?

I am given a code and on one of its pages which shows a "search result" after showing different items, it allows user to click on one of records and it is expected to bring up a page so that specific selected record can be modified.
However, when it is trying to bring up the page I get (by IE) "This page cannot be displayed".
It is obvious the URL is wrong because first I see something http://www.Something.org/Search.aspx then it turns into http://localhost:61123/ProductPage.aspx
I did search in the code and found the following line which I think it is the cause. Now, question I have to ask:
What should I do to avoid using a static URL and make it dynamic so it always would be pointing to the right domain?
string url = string.Format("http://localhost:61123/ProductPage.aspx?BC={0}&From={1}", barCode, "Search");
Response.Redirect(url);
Thanks.
Use HttpContext.Current.Request.Url in your controller to see the URL. Url contains many things including Host which is what you're looking for.
By the way, if you're using the latest .Net 4.6+ you can create the string like so:
string url = $"{HttpContext.Current.Request.Url.Host}/ProductPage.aspx?BC={barCode}&From={"Search"}";
Or you can use string.Format
string host = HttpContext.Current.Request.Url.Host;
string url = string.Format("{0}/ProductPage.aspx?BC={1}&From={2}"), host, barCode, "Search";
You can store the Host segment in your AppSettings section of your Web.Config file (per config / environment like so)
Debug / Development Web.Config
Production / Release Web.Config (with config override to replace the localhost value with something.org host)
and then use it in your code like so.
// Creates a URI using the HostUrlSegment set in the current web.config
Uri hostUri = new Uri(ConfigurationManager.AppSettings.Get("HostUrlSegment"));
// does something like Path.Combine(..) to construct a proper Url with the hostName
// and the other url segments. The $ is a new C# construct to do string interpolation
// (makes for readable code)
Uri fullUri = new Uri(hostUri, $"ProductPage.aspx?BC={barCode}&From=Search");
// fullUrl.AbosoluteUri will contain the proper Url
Response.Redirect(fullUri.AbsoluteUri);
The Uri class has a lot of useful properties and methods to give you Relative Url, AbsoluteUrl, your Url Fragments, Host name etc etc.
This should do it.
string url = string.Format("ProductPage.aspx?BC={0}&From={1}", barCode, "Search");
Response.Redirect(url);
If you are using .Net 4.6+ you can also use this string interpolation version
string url = $"ProductPage.aspx?BC={barcode}&From=Search";
Response.Redirect(url);
You should just be able to omit the hostname to stay on the current domain.

Unescape System.Uri in MonoTouch

When developing my initial MonoTouch iOS application I was trying to use System.Uri instances to download protected resources from a private web service but as all instances are always returning unescaped URLs, my requests are failing when having their signature checked.
Example of a good request:
http://example.com/folder%2Ftest?signature=000%3D
Example of a bad request:
http://example.com/folder/test?signature=000=
In order to properly download my resources I need to convert a good URL from a simple string to a good Uri instance. I have just started the process but I am not able to conclude it:
string urlStringVersion = "http://example.com/folder%2Ftest?signature=000%3D";
//
// urlStringVersion == http://example.com/folder%2Ftest?signature=000%3D
Uri urlUriVersion = new Uri (urlStringVersion);
//
// urlUriVersion == http://example.com/folder/test?signature=000=
var fi = typeof (Uri).GetField ("host", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue (urlUriVersion, "example.com");
//
// urlUriVersion.AbsoluteUri is now == http://example.com/folder/test?signature=000%3D
<Another C# command here>
//
// urlUriVersion.AbsoluteUri is now == http://example.com/folder%2Ftest?signature=000%3D
Finally, which command may I be using to replace the Another C# command here in order to have my final urlUriVersion.AbsoluteUri pointing to the same initial urlStringVersion described URL?
I need this conversion working, otherwise I will be forced to make my resources public at my private web service.
I have also tested some alternatives:
From other questions like: GETting a URL with an url-encoded slash but some exceptions are happening:
System.NullReferenceException : Object reference not set to an instance of an object
Using configuration files but in this case since mobile devices don't technically have an Application Domain they are not available.
Double-encoding the URL by replacing the percent signs with an
encoded percent sign (so '%' becomes '%25').
None of my tries solved my problem.
Thanks in advance,
Piva
The Uri constructor you're using takes an unescaped url string, not an escaped url string.
Try unescaping the url string before creating the Uri instance:
string urlStringVersion = "http://example.com/folder%2Ftest?signature=000%3D";
urlStringVersion = Uri.UnescapeDataString (urlStringVersion);
Uri urlUriVersion = new Uri (urlStringVersion);
If you want to convert a Uri to an (un)escaped string, use the GetComponents method:
var unescaped = urlUriVersion.GetComponents (UriComponents.HttpRequestUri, UriFormat.Unescaped);
var escaped = urlUriVersion.GetComponents (UriComponents.HttpRequestUri, UriFormat.UriEscaped);
Do not change the values of private fields, that is bound to break your app one day. You are circumventing every single test for the Uri class (you're using the class in a way it was not designed for, nor tested), and besides the implementation may change at any time.

Getting full URL from URL with tilde(~) sign

I am trying to get a typical asp.net url starting with the tilde sign ('~') to parse into a full exact url starting with "http:"
I have this string "~/PageB.aspx"
And i want to make it become "http://myServer.com/PageB.aspx"
I know there is several methods to parse urls and get different paths of server and application and such. I have tried several but not gotten the result i want.
Try out
System.Web.VirtualPathUtility.ToAbsolute("yourRelativePath");
There are various ways that are available in ASP.NET that we can use to resolve relative paths to a resource on the server-side and making it available on the client-side. I know of 4 ways -
1) Request.ApplicationPath
2) System.Web.VirtualPathUtility
3) Page.ResolveUrl
4) Page.ResolveClientUrl
Good article : Different approaches for resolving URLs in ASP.NET
If you're in a page handler you could always use the ResolveUrl method to convert the relative path to a server specific path. But if you want the "http://www.yourserver.se" part aswell, you'll have to prepend the Request.Url.Scheme and Request.Url.Authority to it.
string.Format("http://{0}{1}", Request.Url.Host, Page.ResolveUrl(relativeUrl));
This method looks the nicest to me. No string manipulation, it can tolerate both relative or absolute URLs as input, and it uses the exact same scheme, authority, port, and root path as whatever the current request is using:
private Uri GetAbsoluteUri(string redirectUrl)
{
var redirectUri = new Uri(redirectUrl, UriKind.RelativeOrAbsolute);
if (!redirectUri.IsAbsoluteUri)
{
redirectUri = new Uri(new Uri(Request.Url.GetLeftPart(UriPartial.Authority) + Request.ApplicationPath), redirectUri);
}
return redirectUri;
}

Categories