Spoofing HTTP Referrer data using ASP.NET - asp.net

Answers on here and various other sites are often full of warnings not to trust HTTP Referrer headers because they are 'so easily' spoofed or faked.
Before I go any further - no, I'm not up to no good - but I do want to run some referrer-dependant tests.
Whilst I don't doubt that the warnings about fake referrers are true, I can't really find much detailed info on how they can be manipulated. Even the Wikipedia article only talks about it in general terms.
I'm about to play with the RefControl addin for FireFox.
Programatically (in ASP.NET specifically) the UrlReferrer is a read-only property, so I don't see how I can fire off requests with fake referrer data if I can't set it? Do I really have to do it manually?
How would I use ASP.NET to send a request to my site with a user-supplied variable to populate the referrer header?
EDIT : As per my comment below, I ideally want to take an incoming request, manupulate the referrer data and then pass the request on to another page, intact. If I can make it appear intact by building a new one from scratch and copying the original properties, then that is fine too.

I don't know if this exactly what you want, but in general, you should be able to spoof the value of the UrlReferer property (even if it's read-only) in HttpContext.Current.Request by using a bit of reflection.
For example:
FieldInfo fi = HttpContext.Current.Request.GetType().GetField("_referrer", BindingFlags.NonPublic | BindingFlags.Instance);
string initialReferer = HttpContext.Current.Request.UrlReferrer.ToString();
if (fi != null)
fi.SetValue(HttpContext.Current.Request, new Uri("http://example.com"));
string fakedReferer = HttpContext.Current.Request.UrlReferrer.ToString();
On VS; these are the values before and after changing the UrlReferrer:
initialReferer
"http://localhost/Test/Default.aspx"
fakedReferer
"http://example.com/"
If you open the System.Web assembly using ILSpy you'll notice that the UrlReferrer property looks something like this:
public Uri UrlReferrer
{
get
{
if (this._referrer == null && this._wr != null)
{
string knownRequestHeader = this._wr.GetKnownRequestHeader(36);
if (!string.IsNullOrEmpty(knownRequestHeader))
{
try
{
if (knownRequestHeader.IndexOf("://", StringComparison.Ordinal) >= 0)
{
this._referrer = new Uri(knownRequestHeader);
}
else
{
this._referrer = new Uri(this.Url, knownRequestHeader);
}
}
catch (HttpException)
{
this._referrer = null;
}
}
}
return this._referrer;
}
}

This likely isn't going to get you what you want. But you can edit the Referror of an HttpWebRequest. I don't think there is a way of editing the referrer of your request in context.
using System.Net;
HttpWebRequest Req= (HttpWebRequest)System.Net.HttpWebRequest.Create("http://somewhere.com/");
Req.Referer = "http://www.fakesite.com";

Related

how to read additional parameters in alfresco 5.1.1- aikau faceted search

Custom Search UI will be populated when user selects Complex asset in the Advance search screen drop down(apart from Folders,Contents) where 12 fields will be displayed .So when user clicks search button ,need to read those values and redirect to the alfresco repo files(org/alfresco/slingshot/search/search.get.js).We have already customized these files(search.get.js,search.lib.js) existed in the repository to suit out logic and working fine in 4.2.2;As we are migrating to 511,so we need to change this logic in customized faceted-search.get.js to read these values.How to write this logic in customized facted-search.get.js?
It's not actually possible to read those URL hash attributes in the faceted-search.get.js file because the JavaScript controller of the WebScript does not have access to that part of the URL (it only has information about the URL and the request parameters, not the hash parameters).
The hash parameters are actually handled on the client-side by the AlfSearchList widget.
Maybe you could explain what you're trying to achieve so that I can suggest an alternative - i.e. the end goal for the user, not the specifics of the coding you're trying to achieve.
We will be reading the querystring values something like below in the .get.js file.
function getNodeRef(){
var queryString = page.url.getQueryString();
var nodeRef = "NOT FOUND";
var stringArray = queryString.split("&");
for (var t = 0; t < stringArray.length; t++) {
if (stringArray[t].indexOf('nodeRef=') > -1) {
nodeRef = stringArray[t].split('=')[1];
break;
}
}
if (nodeRef !== "NOT FOUND") {
nodeRef = nodeRef.replace("://", "/");
return nodeRef;
}
else {
throw new Error("Node Reference is not found.");
}
}
It may be help you and we will wait for Dave Drapper suggestion also.

How can I protect my asp.net Handler page

I'm using this practice to add comments using AJAX , by sending the data to an ASP.NET Handler which collect the information and then insert the comment, but I am afraid that any one could use it , am I wrong !?
//AddComment.ashx
public void ProcessRequest (HttpContext context) {
CommentsDB db = new CommentsDB();
db.InsertComment(new Comment(context.Request["name"].ToString(), context.Request["comment"].ToString(), "no", int.Parse(context.Request["id"].ToString())));
context.Response.ContentType = "text/plain";
context.Response.Write("succeed");
}
//Comments.js
function AddComment()
{
n = document.getElementById('txtName').value;
c = document.getElementById('txtComment').value;
i = document.getElementById('ctl00_ContentPlaceHolder1_thread').value;
m = document.getElementById('ctl00_ContentPlaceHolder1_Label1');
if(n == "" || c == "" || n.length > 100 || c.length > 400)
{
m.innerHTML = "<center><font color=black size=3><b><font color=red>*</font> An error has occurred</b></font></center><br>";
return;
}
m.innerHTML = "";
document.getElementById('btn').disabled = true;
$.post("./Handlers/AddComment.ashx", {'name':n, 'comment':c, 'id':i}, function(Response){
m.innerHTML = "<center><font color=black size=3><b>accepted</b> <img src=./Images/success-icon.png></font></center><br>";
});
}
Your assumption is correct, that your users can potentially make their own HTTP requests to your handler, and provide bogus data. They could also manipulate your page markup in their browsers (with any developer toolbar) and do the same.
So, you're going to want to do some validation on your server side if you're worried about this. If your application requires authentication, just look up the current user's name in the handler's ProcessRequest method, rather than posting it.
I think that's what your question is getting at. Also, clean up your markup, center and font tags are deprecated.
If you require that the commenters to be logged in than check for the actual user (stored on the web server - in session for example).
Or if you allow non authenticated comments, than consider using some captcha to protect against automated requests.

Setting the Content-Type of an empty response in ASP.NET MVC

In order to support a legacy application that's in the field, I need my ASP.NET MVC app to return an empty response that also has a Content-Type. One of IIS, ASP.NET, or ASP.NET MVC is removing my Content-Type when I send back a null response. Is there any way around this?
(While not requiring an empty response with a set Content-Type would obviously be the ideal solution, the clients are already out there, and many of them cannot be upgraded.)
EDIT: Since there was a request for code: I'm proxying the request from the new web application to the one that older clients rely on. To do this, I have a subclass of ActionResult, called LegacyResult, that you can simply return for those methods that need to be handled by the old software. This is the relevant part of its code:
public override void ExecuteResult(ControllerContext context)
{
using (var legacyResponse = GetLegacyResponse(context))
{
var clientResponse = context.HttpContext.Response;
clientResponse.Buffer = false;
clientResponse.ContentType = legacyResponse.ContentType; /* Yes, I checked that legacyResponse.ContentType is never string.IsNullOrEmpty */
if (legacyResponse.ContentLength >= 0) clientResponse.AddHeader("Content-Length", legacyResponse.ContentLength.ToString());
var legacyInput = legacyResponse.GetResponseStream();
using (var clientOutput = clientResponse.OutputStream)
{
var rgb = new byte[32768];
int cb;
while ((cb = legacyInput.Read(rgb, 0, rgb.Length)) > 0)
{
clientOutput.Write(rgb, 0, cb);
}
clientOutput.Flush();
}
}
}
If legacyInput has data, then Content-Type is set appropriately. Otherwise, it's not. I can actually kluge the old backend to send an empty v. non-empty response for exactly the same request, and observe the difference in Fiddler.
EDIT 2: Poking around with Reflector reveals that, if headers have not been written at the time that HttpResponse.Flush is called, then Flush writes out the headers itself. The problem is that it only writes out a tiny subset of the headers. One of the missing ones is Content-Type. So it seems that, if I can force headers out to the stream, I can avoid this problem.
You have to trick the response into writing the headers, by falsely telling it there's content, then suppressing it:
/// [inside the writing block]
var didWrite = false;
while ((cb = legacyInput.Read(rgb, 0, rgb.Length)) > 0)
{
didWrite = true;
clientOutput.Write(rgb, 0, cb);
}
if (!didWrite)
{
// The stream needs a non-zero content length to write the correct headers, but...
clientResponse.AddHeader("Content-Length", "1");
// ...this actually writes a "Content-Length: 0" header with the other headers.
clientResponse.SuppressContent = true;
}

Is it safe to use Request.ApplicationPath for cookie path

Is it safe to use such code?
Response.Cookies[cookieName].Path = Request.ApplicationPath + "/";
I want to know about all corner cases, please...
In short, no, it's not safe. Using cookie paths is fraught with problems as they are case sensitive in IE and Chrome, but not FF. This means any mismatch in path case will stuff things up.
When generating a cookie, if the path you set differs in case from what the user typed, browsers won't store it.
When the user returns, if the path they enter differs in case from the first trip, the browser won't supply the cookie with the request.
What problem are you trying to solve?
If your application runs in the root of a domain, Request.ApplicationPath == "/". Hence, with your code, the path of your cookie will be //. You can dodge around this problem by doing this:
cookie.Path = Request.ApplicationPath;
if (cookie.Path.Length > 1) cookie.Path += '/';
As Will correctly points out, you will want to make sure that your application enforces a consistent casing of URLs (i.e. redirect all requests with URLs containing uppercase letters to their lowercase equivalent).
Other than that, I believe you should be fine doing this. If you want all of your cookies to be "application scoped", consider creating a custom IHttpModule with code like this (or extend global.asax.cs):
private void Application_EndRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
var cookiePath = app.Request.ApplicationPath;
if (cookiePath.Length > 1) cookiePath += '/';
foreach (string name in app.Response.Cookies.AllKeys)
{
var cookie = app.Response.Cookies[name];
cookie.Path = cookiePath;
}
}
No, it's not safe, for the reasons that Will specified.
But... You may want to employ this technique to fulfill your intent.

Flex 3 - how to support HTTP Authentication URLRequest?

I have a Flex file upload script that uses URLRequest to upload files to a server. I want to add support for http authentication (password protected directories on the server), but I don't know how to implement this - I assume I need to extend the class somehow, but on how to I'm a little lost.
I tried to modify the following (replacing HTTPService with URLRequest), but that didn't work.
private function authAndSend(service:HTTPService):void{
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("someusername:somepassword");
service.headers = {Authorization:"Basic " + encoder.toString()};
service.send();
}
I should point out that I'm not knowledgeable when it comes to ActionScript / Flex, although I have managed to successfully modify the upload script somewhat.
[Edit] - here is an update of my progress, based on the answer below, although I still cannot get this to work:
Thank you for your assistance. I've tried to implement your code but I've not had any luck.
The general behaviour I'm experiencing when dealing with HTTP authenticated locations is that with IE7 all is well but in Firefox when I attempt to upload a file to the server it displays an HTTP authentication prompt - which even if given the correct details, simply stalls the upload process.
I believe the reason IE7 is ok is down to the the session / authentication information being shared by the browser and the Flash component - however, in Firefox this is not the case and I experience the above behaviour.
Here is my updated upload function, incorporating your changes:
private function pergress():void
{
if (fileCollection.length == 0)
{
var urlString:String = "upload_process.php?folder="+folderId+"&type="+uploadType+"&feid="+formElementId+"&filetotal="+fileTotal;
if (ExternalInterface.available)
{
ExternalInterface.call("uploadComplete", urlString);
}
}
if (fileCollection.length > 0)
{
fileTotal++;
var urlRequest:URLRequest = new URLRequest("upload_file.php?folder="+folderId+"&type="+uploadType+"&feid="+formElementId+"&obfuscate="+obfuscateHash+"&sessidpass="+sessionPass);
urlRequest.method = URLRequestMethod.POST;
urlRequest.data = new URLVariables("name=Bryn+Jones");
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("testuser:testpass");
var credsHeader:URLRequestHeader = new URLRequestHeader("Authorization", "Basic " + encoder.toString());
urlRequest.requestHeaders.push(credsHeader);
file = FileReference(fileCollection.getItemAt(0));
file.addEventListener(Event.COMPLETE, completeHandler);
file.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
file.addEventListener(ProgressEvent.PROGRESS, onUploadProgress);
file.upload(urlRequest);
}
}
As stated above, I seem to be experiencing the same results with or without the amendments to my function.
Can I ask also where the crossdomain.xml should be located - as I do not currently have one and am unsure where to place it.
The syntax is a little different for URLRequest, but the idea's the same:
private function doWork():void
{
var req:URLRequest = new URLRequest("http://yoursite.com/yourservice.ext");
req.method = URLRequestMethod.POST;
req.data = new URLVariables("name=John+Doe");
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("yourusername:yourpassword");
var credsHeader:URLRequestHeader = new URLRequestHeader("Authorization", "Basic " + encoder.toString());
req.requestHeaders.push(credsHeader);
var loader:URLLoader = new URLLoader();
loader.load(req);
}
A couple of things to keep in mind:
Best I can tell, for some reason, this only works where request method is POST; the headers don't get set with GET requests.
Interestingly, it also fails unless at least one URLVariables name-value pair gets packaged with the request, as indicated above. That's why many of the examples you see out there (including mine) attach "name=John+Doe" -- it's just a placeholder for some data that URLRequest seems to require when setting any custom HTTP headers. Without it, even a properly authenticated POST request will also fail.
Apparently, Flash player version 9.0.115.0 completely blocks all Authorization headers (more information on this one here), so you'll probably want to keep that in mind, too.
You'll almost surely have to modify your crossdomain.xml file to accommodate the header(s) you're going to be sending. In my case, I'm using this, which is a rather wide-open policy file in that it accepts from any domain, so in your case, you might want to limit things a bit more, depending on how security-conscious you are.
crossdomain.xml:
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
<allow-http-request-headers-from domain="*" headers="Authorization" />
</cross-domain-policy>
... and that seems to work; more information on this one is available from Adobe here).
The code above was tested with Flash player 10 (with debug & release SWFs), so it should work for you, but I wanted to update my original post to include all this extra info in case you run into any issues, as the chances seem (sadly) likely that you will.
Hope it helps! Good luck. I'll keep an eye out for comments.
The FileReference.upload() and FileReference.download() methods do not support the URLRequest.requestHeaders parameter.
http://livedocs.adobe.com/flex/2/langref/flash/net/URLRequest.html
If you want to upload a file, you just need to send the correct headers and the content of file using URLRequest via UploadPostHelper class. This works 100%, i am using this class to upload generated images and CSV files, but you could upload any kind of file.
This class simply prepares the request with headers and content as if you would be uploading the file from a html form.
http://code.google.com/p/as3asclublib/source/browse/trunk/net/UploadPostHelper.as?r=118
_urlRequest = new URLRequest(url);
_urlRequest.data = "LoG";
_urlRequest.method = URLRequestMethod.POST;
_urlRequest.requestHeaders.push(new URLRequestHeader("X-HTTP-Code-Override", "true"));
_urlRequest.requestHeaders.push(new URLRequestHeader("pragma", "no-cache"));
initCredentials();
_loader.dataFormat = URLLoaderDataFormat.BINARY;
//this creates a security problem, putting the content type in the headers bypasses this problem
//_urlRequest.contentType = 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary();
_urlRequest.requestHeaders.push( new URLRequestHeader( 'Cache-Control', 'no-cache' ) );
_urlRequest.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary()));
_urlRequest.data = UploadPostHelper.getPostData("file.csv", param[1]);
_loader.load(_urlRequest);
I'm not sure about this but have you tried adding username:password# to the beginning of your url?
"http://username:password#yoursite.com/yourservice.ext"
var service : HTTPService = new HTTPService ();
var encoder:Base64Encoder = new Base64Encoder();
encoder.insertNewLines = false;
encoder.encode("user:password");
service.headers = {Authorization:"Basic " + encoder.toString()};
service.method = HTTPRequestMessage.POST_METHOD;
service.request = new URLVariables("name=John+Doe");
service.addEventListener(FaultEvent.FAULT,error_handler );
service.addEventListener(ResultEvent.RESULT,result_handler);
service.url = 'http://blah.blah.xml?'+UIDUtil.createUID();
service.send();
Seemingly similar problem was solved here. I urge you to also check the Flexcoders post linked to in the first post.
The problem was that FireFox uses a separate browser window instance to send the file upload request. The solution is to manually attach the session id to the request url. The session id is not attached as a regular GET variable, but with a semicolon (the reason for this syntax is unknown to me).
Flash is very limited in terms of what sort of headers you can pass with an http request (and it changes between browsers and OSes). If you get this to work on one browser/OS, make sure you test it on the others.
The best thing to do is not mess with HTTP headers.
We have the same issue (uploading to Picasa Web Albums from flash) and post through a proxy on our server. We pass the extra headers through as post parameters and our proxy does the right thing.
"http://username:password#yoursite.com/yourservice.ext"
This doesn't work in IE (http://www.theregister.co.uk/2004/01/30/ms_drop_authentication_technique/) and doesn't seem to work in Chrome either.
probably not usable in Flash
Here is a work-around when using ASP.Net based in part on the work here.
I built a component that dynamically writes Flex objects to the page so they can be used in UpdatePanels. Message me if you want they code. To solve the above problem in pages where authentication cookies will need to be sent by URLRequest, I add the values in as flashVars.
This code only works in my object, but you get the idea
Dictionary<string, string> flashVars = new Dictionary<string, string>();
flashVars.Add("auth", Request.Cookies["LOOKINGGLASSFORMSAUTH"].Value);
flashVars.Add("sess", Request.Cookies["ASP.NET_SessionId"].Value);
myFlexObject.SetFlashVars(flashVars);
Then in the Flex Object, check for the params
if (Application.application.parameters.sess != null)
sendVars.sess= Application.application.parameters.sess;
if (Application.application.parameters.auth != null)
sendVars.au= Application.application.parameters.auth;
request.data = sendVars;
request.url = url;
request.method = URLRequestMethod.POST;
Finally stuff the cookies in on global.asax BeginRequest
if (Request.RequestType=="POST" && Request.Path.EndsWith("upload.aspx"))
{
try
{
string session_param_name = "sess";
string session_cookie_name = "ASP.NET_SESSIONID";
string session_value = Request.Form[session_param_name]; // ?? Request.QueryString[session_param_name];
if (session_value != null) { UpdateCookie(session_cookie_name, session_value); }
}
catch (Exception) { }
try
{
string auth_param_name = "au";
string auth_cookie_name = FormsAuthentication.FormsCookieName;
string auth_value = Request.Form[auth_param_name];// ?? Request.QueryString[auth_param_name];
if (auth_value != null) { UpdateCookie(auth_cookie_name, auth_value); }
}
catch (Exception) { }
}
Hope this help someone avoid the 6 hours I just spent addressing this. Adobe has closed the issue as unresolvable, so this was my last resort.

Resources