I'm trying to call a webservice. This webservice call depends on the user input as the URL.
The URL looks like follows:
https://someurl.com/somefunction/{userinput}
And my function looks like this
public async Task<Data> GetData(string input)
{
try
{
Address = BaseAddress; // https://someurl.com/somefunction/{userinput}
Address = Address.Replace("{userinput}", input);
....
WebService ws = await base.GetData(httpClient, serverIPaddress);
....
}
}
And I get the security error from Fortify
Server-Side Request Forgery (Input Validation and Representation, Data
Flow)
The function GetAsync() on line 122 initiates a network connection to
a third-party system using user-controlled data for resource URI. An
attacker may leverage this vulnerability to send a request on behalf
of the application server since the request will originate from the
application server's internal IP address.
Here are the recommendations:
Recommendations:
Do not establish network connections based on user-controlled data and
ensure that the request is being sent to the expected destination. If
user data is necessary to build the destination URI, use a level of
indirection: create a list of legitimate resource names that a user is
allowed to specify, and only allow the user to select from the list.
With this approach the input provided by the user is never used
directly to specify the resource name.
In some situations this approach is impractical because the set of
legitimate resource names is too large or too hard to keep track of.
Programmers often resort to blacklisting in these situations.
Blacklisting selectively rejects or escapes potentially dangerous
characters before using the input. However, any such list of unsafe
characters is likely to be incomplete and will almost certainly become
out of date. A better approach is to create a whitelist of characters
that are allowed to appear in the resource name and accept input
composed exclusively of characters in the approved set.
Also, if required, make sure that the user input is only used to
specify a resource on the target system but that the URI scheme, host,
and port is controlled by the application. This way the damage that an
attacker is able to do will be significantly reduced.
But the thing is that I really need to change {userinput} based on the supplied data from user. {userinput} will be a string with a certain maximum length.
How to resolve this issue?
After so much research and hit and try attempts, I finally got this working by appending the input in the BaseAddress
The sample code looks like
public async Task<Data> GetData(string input)
{
try
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri($"https://someurl.com/somefunction/{input}");
var content = await httpClient.GetStringAsync("");
return JsonConvert.DeserializeObject<Data>(content);
}
}
Related
I have a web site (IIS, C#.Net, MVC4) where users are (forms-)authenticated and they upload media files (mostly .mp4) and authorize set of users to play back on demand. I store these files on local storage.
I play these files using jwplayer back to the authorized users on demand.
jwplayer expects I pass the url directly for it to play, but I didn't want to expose a direct url.
I really have to restrict unauthorized access to these files as they are private files.
I tried implementing a controller method to handle https://mysite/Video/Watch?VideoId=xyz, and return FileStream of the actual file. It works on a browser directly. (Though not sure how efficient it is for large files.)
But the problem is, jwplayer looks for urls of pattern http(s)://domain/path/file.mp4[?parameter1=value1¶meter2=value2 and so on.]
When I give a url like https://mysite/Video/Watch?VideoId=xyz, it says 'No playable sources found' without even sending a HEAD request.
If I expose the urls directly, the files are available for anybody to download, which will break the privacy.
Worst case, I would at least want to avoid hot links which will live for ever.
I have also looked at www.jwplayer.com/blog/securing-your-content/ but did not find the solutions suitable.
My questions are,
Is there a way I can retain the pattern of the url http(s)://domain/path/file.mp4 and still control the access to the file?
If (1.) is not possible, how do I leverage the parameters that could be passed on the url. With the parameters, I can think of signed urls. What should I do on the server if I have to provide and handle/validate signed urls.
Just not to hinder the performance, after any validation, can I somehow get the iis to handle the filestream rather my code?
I implemented an HTTPModule to allow/block access to the file. This addresses my questions 1 & 3.
Code snippet below.
void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
//Get the file extension
string fileExt= Path.GetExtension(app.Request.Url.AbsolutePath);
//Check if the extension is mp4
bool requestForMP4 = fileExt.Equals(".mp4", StringComparison.InvariantCultureIgnoreCase);
//If the request is not for an mp4 file, we have nothing to do here
if (!requestForMP4)
return;
//Initially assume no access to media
bool allowAccessToMedia = false;
//....
// Logic to determine access
// If allowed set allowAccessToMedia = true
// otherwise, just return
//....
if(!allowAccessToMedia)
{
//Terminate the request with HTTP StatusCode 403.2 Forbidden: Read Access Forbidden
app.Response.StatusCode = (int)HttpStatusCode.Forbidden;
app.Response.SubStatusCode = 2;
app.CompleteRequest();
}
}
I'm trying to implement punchout catalogs on our eComm site. Honestly, the documentation for cXML is a mess and all the code examples are in javascript and/or VB.Net (I use C# and would rather not have to try and translate). Does anyone out there have examples or samples of how to receive the PunchOutSetupRequest XML and then send out the PunchOutSetupResponse XML using C#? I've been unable to find anything on the interwebs (I've been looking for two days now)...
I'm hoping I can just do this inside an ActionResult (vs. a 'launch page' as suggested).
I'm a complete noob at punchouts and could really use some help here. The bosses are being pretty pushy, so any assistance would be greatly appreciated. Suggestions as to how to make this work would also be much appreciated.
I apologize to all for the vagueness of the question (request).
This isn't trivial, but this should get you started.
You'll need 3 generic handlers (.ashx): Setup, Start, and Order....
Setup and Order will receive HTTP Post with content-type of "text/xml". Look at HttpRequest.InputStream if needed to get the XML into a string. From there, look at LINQ-to-XML to dig out the data you want. Your HTTP Response to both of these will also be content-type "text/xml" and UTF8 encoded, returning the CXML as documented...use LINQ-to-XML to produce that.
The Setup handler will need to validate credentials and return a URL with a unique QueryString token pointing to the Start handler. Do not expect session persistence between Setup and Start, because they're not from the same caller. This handler will need to create an application object for the token and associated data you extracted from the cXML.
The Start handler will be called as a simple GET, and will need to match the token in the QueryString to the appropriate application object, copy that data to the session, and then do a response.redirect to whatever page in your site you want the buyer to land on.
Once they populate their cart with some things, and are ready to check out, you'll take them to a page that has an embedded form (not to be confused with an ASP.Net form that posts back to your server) and a submit button (again, not an ASP.Net button). From your Setup handler, you captured a URL to point this form's Post, and within the form you'll have a hidden input tag with the UTF8 encoded CXML Punchout Order injected as the value produced with LINQ-to-XML. Highly recommend Base64 encoding that value to avoid ASP.Net messing with the tags it contains during rendering, and naming the hidden input "cxml-base64" per the documentation. The result is the form is client-side POSTed to your customer's server instead of yours, and their server will extract the CXML Punchout Order and that ends your visitor's session.
The Order handler will receive a CXML OrderRequest and just like Setup, you'll dump that to a string and then use LINQ-to-XML to parse it and act upon it. Again you'll get credentials to verify, possibly a credit card to process, and the order items, ship-to, etc. Note that the OrderRequest may not contain all the items that were in the Punchout Order, because the system on your customer's side may remove items or even change item quantities before submitting the final OrderRequest to you. The OrderRequest could come back to you after the Punchout Order is posted to them in a matter of minutes, days, weeks, or never...don't bother storing the cart data in hopes of matching it to the order later.
Last note...the buyer may be experiencing your site in an iframe embedded in their web-based procurement UI, so design accordingly.
If you need more info, reply to this and I'll get back.
Update...Additional considerations:
Discuss with the buyer how they want fault handling to flow, particularly with orders, because you have a choice. 1) exhaustively evaluate everything in the CXML you receive and return response codes other than 200 if anything is wrong, or 2) always return a 200 Success and deal with any issues out of band or by generating a ConfirmationRequest that rejects the order. My experience is that a mix of the two works best. Certainly you should throw a non-200 if the credentials fail, but you may not want (or be able) to run a credit card or validate stock availability inline. Your buyer's system may not be able to cope with dozens of possible faults, and/or may not show your fault messages to the user for them to make corrections. I've seen systems that will flat-out discard any non-200 response code and just blindly retry the submission repeatedly on an interval for hours or days until it gives up on a sanity check, while others will handle response codes within certain ranges differently than others, for example a 4xx invokes a retry, while a 5xx is treated as fatal. Remember that Setup and Order are not coming directly from the user...their procurement system is generating those internally.
Update...answering the comment about how to test things...
You'd use the same method as you will for generating outbound ConfirmationRequest, ShipNoticeRequest, and InvoiceDetailRequest, all of which generally are produced on your side after receiving an OrderRequest from your customer's procurement system.
Start with Linq-To-XML for an example of crafting your outgoing cXML (Creating XML Trees section). Combine that example with this bit of code:
StringBuilder output = new StringBuilder();
XmlWriterSettings objXmlWriterSettings = new XmlWriterSettings();
objXmlWriterSettings.Indent = true;
objXmlWriterSettings.NewLineChars = Environment.NewLine;
objXmlWriterSettings.NewLineHandling = NewLineHandling.Replace;
objXmlWriterSettings.NewLineOnAttributes = false;
objXmlWriterSettings.Encoding = new UTF8Encoding();
using (XmlWriter objXmlWriter = XmlWriter.Create(output, objXmlWriterSettings)) {
XElement root = new XElement("Root",
new XElement("Child", "child content")
);
root.Save(objXmlWriter);
}
Console.WriteLine(output.ToString());
So at this point the StringBuilder (output) has your whole cXML, and you need to POST it someplace. Your Web Application project, started with F5 and a default.aspx page will be listening on localhost and some port (you'll see that in the URL it opens). Separately, perhaps using VS Express for Desktop, you have the above code in a console app that you can run to do the Post using something like this:
Net.HttpWebRequest objRequest = Net.WebRequest.Create("http://localhost:12345/handler.ashx");
objRequest.Method = "POST";
objRequest.UserAgent = "Some User Agent";
objRequest.ContentLength = output.Length;
objRequest.ContentType = "text/xml";
IO.StreamWriter objStreamWriter = new IO.StreamWriter(objRequest.GetRequestStream, System.Text.Encoding.ASCII);
objStreamWriter.Write(output);
objStreamWriter.Flush();
objStreamWriter.Close();
Net.WebResponse objWebResponse = objRequest.GetResponse();
XmlReaderSettings objXmlReaderSettings = new XmlReaderSettings();
objXmlReaderSettings.DtdProcessing = DtdProcessing.Ignore;
XmlReader objXmlReader = XmlReader.Create(objWebResponse.GetResponseStream, objXmlReaderSettings);
// Pipes the stream to a higher level stream reader with the required encoding format.
IO.MemoryStream objMemoryStream2 = new IO.MemoryStream();
XmlWriter objXmlWriter2 = XmlWriter.Create(objMemoryStream2, objXmlWriterSettings);
objXmlWriter2.WriteNode(objXmlReader, true);
objXmlWriter2.Flush();
objXmlWriter2.Close();
objWebResponse.Close();
// Reset current position to the beginning so we can read all below.
objMemoryStream2.Position = 0;
StreamReader objStreamReader = new StreamReader(objMemoryStream2, Encoding.UTF8);
Console.WriteLine(objStreamReader.ReadToEnd());
objStreamReader.Close();
Since your handler should be producing cXML you'll see that spat out in the console. If it pukes, you'll get a big blob of debug mess in the console, which of course will help you fix whatever is broken.
pardon the verbosity in the variable names, done to try to make things clear.
This is a follow-up to Choosing a Connection String based on kind of request for which I got no answer and what I thought worked doesn't.
I have a webservice that needs to choose a specific connection string based on the user calling it from a browser or from a client application.
I tried:
HttpContext.Current != null? ConnectionStrings["Website"].ConnectionString : ConnectionStrings["Client"].ConnectionString
but realized that at some point even if I'm using the client application, there is some HttpContext (if someone can explain why it'd be great) but the Browser field under Request is "Unknown". So, then I tried:
if ( HttpContext.Current != null )
{
if ( HttpContext.Current.Request.Browser != "Unknown" )
{
//browser connection string here
}
else
//client app connection string here
}
else
//client app connection string here
This worked wonders when debugging, but on testing environment it still points to Browser connection string even when calling from the client app, as if at some point the Browser isn't "Unknown" ...
Is there a MUCH easier/simpler way to do this? The way I'm doing it seems really ugly.
I'm quite desperate at the moment as I have no idea why this is happening..
Rather than detecting and switching on the browser type, consider these two suggestions:
Add Custom Request Headers
In your various callers, define a new custom header in your Http request.
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Headers.Add("CallerType", "ClientApp"); // "Browser", etc.
Then you know exactly and reliably what type of client is calling. This would be hard to get wrong, and couldn't be spoofed/mistaken.
Include The Caller Type in the QueryString
myService.asmx?BrowserType=1
Add a simple new querystring parameter to your .asmx webmethod. This will work just the same in a controlled environment, but if other users/developers get it wrong, or malform the expected values, you'd have to take other measures to correct/handle.
Both allow you to easily determine the connString on the incoming value. Perhaps the absense of a modifier/header, you could assume a default. Your sample question has 2 basic outcomes, and either suggested solution will be easy to extend (browser, client app, iPhone, whathaveyou).
I created a simple .NET WebService (it just passes back a string). How do I modify the server side (and possibly the client side) so that it also uses a username/password to validate before sending a response?
Client Code:
static void Main(string[] args)
{
UpdateClient client = new UpdateClient("UpdateSOAPIIS");
client.ClientCredentials.UserName.UserName = "Michael";
client.ClientCredentials.UserName.Password = "testpassword";
String response = client.GetString("New York, NY");
Console.WriteLine(response);
if (client != null) client.Close();
}
Server Code:
public virtual GetStringResponse GetString(GetStringRequest request)
{
return new GetStringResponse("Search Location: " + request.location);
}
I recommend reading Juval Lowy's excellent article Declarative WCF Security. He describes five common scenarios (intranet, internet, b2b, anonymous, no security at all) and shows what that means, how to accomplish that etc.
He even goes as far as creating declarative attributes that you can basically just put on your service declaration and be done with it.
Those security scenario should really cover at least 80%, if not 95% of your typical cases. Study them and use them! Highly recommended
It really depends on what kind of security you want. Should the protocol be encrypted, should the data be encrypted, or do you just want to authenticate a user. In the last case you can just go ahead and use whatever technology you want to verify that the user has permissions to use the API. For other options and some code, check out this MSDN article http://msdn.microsoft.com/en-us/library/ms731925.aspx
I would like to take the original URL, truncate the query string parameters, and return a cleaned up version of the URL. I would like it to occur across the whole application, so performing through the global.asax would be ideal. Also, I think a 301 redirect would be in order as well.
ie.
in: www.website.com/default.aspx?utm_source=twitter&utm_medium=social-media
out: www.website.com/default.aspx
What would be the best way to achieve this?
System.Uri is your friend here. This has many helpful utilities on it, but the one you want is GetLeftPart:
string url = "http://www.website.com/default.aspx?utm_source=twitter&utm_medium=social-media";
Uri uri = new Uri(url);
Console.WriteLine(uri.GetLeftPart(UriPartial.Path));
This gives the output: http://www.website.com/default.aspx
[The Uri class does require the protocol, http://, to be specified]
GetLeftPart basicallys says "get the left part of the uri up to and including the part I specify". This can be Scheme (just the http:// bit), Authority (the www.website.com part), Path (the /default.aspx) or Query (the querystring).
Assuming you are on an aspx web page, you can then use Response.Redirect(newUrl) to redirect the caller.
Here is a simple trick
Dim uri = New Uri(Request.Url.AbsoluteUri)
dim reqURL = uri.GetLeftPart(UriPartial.Path)
Here is a quick way of getting the root path sans the full path and query.
string path = Request.Url.AbsoluteUri.Replace(Request.Url.PathAndQuery,"");
This may look a little better.
string rawUrl = String.Concat(this.GetApplicationUrl(), Request.RawUrl);
if (rawUrl.Contains("/post/"))
{
bool hasQueryStrings = Request.QueryString.Keys.Count > 1;
if (hasQueryStrings)
{
Uri uri = new Uri(rawUrl);
rawUrl = uri.GetLeftPart(UriPartial.Path);
HtmlLink canonical = new HtmlLink();
canonical.Href = rawUrl;
canonical.Attributes["rel"] = "canonical";
Page.Header.Controls.Add(canonical);
}
}
Followed by a function to properly fetch the application URL.
Works perfectly.
I'm guessing that you want to do this because you want your users to see pretty looking URLs. The only way to get the client to "change" the URL in its address bar is to send it to a new location - i.e. you need to redirect them.
Are the query string parameters going to affect the output of your page? If so, you'll have to look at how to maintain state between requests (session variables, cookies, etc.) because your query string parameters will be lost as soon as you redirect to a page without them.
There are a few ways you can do this globally (in order of preference):
If you have direct control over your server environment then a configurable server module like ISAPI_ReWrite or IIS 7.0 URL Rewrite Module is a great approach.
A custom IHttpModule is a nice, reusable roll-your-own approach.
You can also do this in the global.asax as you suggest
You should only use the 301 response code if the resource has indeed moved permanently. Again, this depends on whether your application needs to use the query string parameters. If you use a permanent redirect a browser (that respects the 301 response code) will skip loading a URL like .../default.aspx?utm_source=twitter&utm_medium=social-media and load .../default.aspx - you'll never even know about the query string parameters.
Finally, you can use POST method requests. This gives you clean URLs and lets you pass parameters in, but will only work with <form> elements or requests you create using JavaScript.
Take a look at the UriBuilder class. You can create one with a url string, and the object will then parse this url and let you access just the elements you desire.
After completing whatever processing you need to do on the query string, just split the url on the question mark:
Dim _CleanUrl as String = Request.Url.AbsoluteUri.Split("?")(0)
Response.Redirect(_CleanUrl)
Granted, my solution is in VB.NET, but I'd imagine that it could be ported over pretty easily. And since we are only looking for the first element of the split, it even "fails" gracefully when there is no querystring.