Is it safe to use Request.ApplicationPath for cookie path - asp.net

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.

Related

ASP.NET Facebook app session issue with safari

Currently I am working on a Facebook app and it's developed by using ASP.NET.
This app works fine with IE(7,8 and 9) FF and Chrome.
The first page is default.aspx and it will handle the authentication then redirect to home.aspx
Now the only issue it has is that Safari doesn't accept cross-domain cookies. I've changed the web.config file and add it in order to avoid the use of cookies.
After that, the URL comes to
http://www.testdomain.com/(S(gvsc2i45pqvzqm3lv2xoe4zm))/default.aspx
It just can't be redirect from default.aspx to home.aspx automatically...
Anyone got a clue?
Or, is there anyway that i can deal with Safari with ASP.Net session in Facebook app?
Tons of thanks
PS. The code from default.aspx page
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (!string.IsNullOrEmpty(Request.Params["signed_request"]))
{
string signed_request = Request.Params["signed_request"];
if (!string.IsNullOrEmpty(signed_request))
{
// split signed request into encoded signature and payload
string[] signedRequestParts = signed_request.Split('.');
string encodedSignature = signedRequestParts[0];
string payload = signedRequestParts[1];
// decode signature
string signature = decodeSignature(encodedSignature);
// calculate signature from payload
string expectedSignature = hash_hmac(payload, Facebook.FacebookApplication.Current.AppSecret);
if (signature == expectedSignature)
{
// signature was not modified
Dictionary<string, string> parameters = DecodePayload(payload);
if (parameters != null)
{
string UserId = parameters["user_id"];
Session.Add("UserId", _SystemUser.SystemUserId);
Session.Add("Username", _SystemUser.Username);
Response.Redirect("Home.aspx?user_id=" + UserId);
}
}
}
}
if (!String.IsNullOrEmpty(Request["error_reason"])) // user denied your request to login
{
logger.Debug("Error Reason: " + Request["error_reason"]);
//User denied access
}
if (String.IsNullOrEmpty(Request["code"])) // request to login
{
string url1 = String.Format("https://www.facebook.com/dialog/oauth?client_id={0}&redirect_uri={1}&scope={2}", Facebook.FacebookApplication.Current.AppId, callbackUrl, ext_perms);
Response.Redirect(url1);
}
}
}
When using cookieless sessions, ASP.Net will automatically redirect any requests without a session ID in the URL to the same page, but with a new SessionID in the URL. However, it redirects as a GET request, and thus does not forward on any POSTED parameters ... so after the redirect your "parameters" variable, from the decoded signed_request, will be missing because the page will no longer have the signed_request POSTed parameter.
There are two possible solutions to this (that I know of):
Intercept the initial redirect in Global.ascx, and instead do your own redirect with the new SessionID in the URL ... BUT, do this as a self-posting form in Javascript where the form also has a signed_request param with the value of the signed_request.
Turn cookie sessions back on, and in your first page redirect out of FB to a page. In this page set a Session variable (which will get ASP.Net to set a session cookie), and then redirect back into FB.
You may/will also need some code to handle any app_data, if this is on a tab page too.
Sorry I can't be more useful code wise. I've written my own handlers for my job, but my workplace now owns that code! I'm never sure how much is OK to share.
I used cookieless session, but as the initial page was getting refreshed, the Facebook "signed_request" POSTed to the landing page was lost.
As a workaround, I added an HTTPModule to override EndRequest() event. In the event if the page is "initial page" & contained "signed_request" POSTed, the value is added as querystring. In the page we would check the querystring value and set it into session, to be used in the application.
The EndRequest is as below:
void context_EndRequest(object sender, EventArgs e)
{
HttpContext cntxt = HttpContext.Current;
const string paramname = "signed_request";
const string initialPage= "/startapp.aspx";
if ((String.Compare(cntxt.Request.Url.AbsolutePath, initialPage, true) == 0) && (!String.IsNullOrEmpty(cntxt.Request[paramname])))
{
string strQuerySignedReq = paramname+"=" + cntxt.Request[paramname];
if (cntxt.Response.RedirectLocation.Contains(".aspx?"))
cntxt.Response.RedirectLocation = cntxt.Response.RedirectLocation + "&" + strQuerySignedReq;
else
cntxt.Response.RedirectLocation = cntxt.Response.RedirectLocation + "?" + strQuerySignedReq;
}
}
The initial page - "startapp.aspx", load event would be:
protected void Page_Load(object sender, EventArgs e)
{
signed_request = Request.QueryString["signed_request"];
}
The disadvantage of the code is that EndRequest() would execute for all requests. Also, only relative url should be used for links. I have had several annoying experiences on cookies and Facebook, due to various security levels on different browsers. Hence, I can live with the disadvantages. Hope this helps!
I know this is an old question, but I had exactly the same problem and found a solution.
The solution here works if you're using a SQL Server in your application.
Using cookieless to store your SessionId in the URL will avoid the cookie problem, but still missing the Session issue in Safari.
Well, you'll need to set a SQL SessionState, this will make your application communicate with your Database to store the Sessions. This will work for facebook canvas apps in Safari.
Setting this is simple:
Registering: run aspnet_regsql.exe (in C:/Windows/Microsoft.NET/Framework/'Framework version'/)
Check parameters in https://msdn.microsoft.com/en-us/library/ms229862.aspx (the main ones are -S –ssadd)
In the same path, there is a InstallSqlState.SQL script. Run it on your Database Server.
Now, set this tag in your Web.Config file:
<configuration>
<system.web>
<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" timeout="120" cookieless="true" />
</system.web>
</configuration>
And the magic is done!
There is something to remember. You can't do WebRequests to facebook from Server side to request for access tokens, because facebook redirects the calls to the "Valid OAuth redirect URIs", and completely ignores the SessionId parameters in the Request URI. You still can make WebRequests to APIs, but the authentication will need to be assyncronous, using Javascript.

Spoofing HTTP Referrer data using 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";

asp.net url minus pagename and querystring

I've been looking around the web for a simple and straight forward solution for the following problem but I cant seem to find anything that suits my needs.
I have an asp.net site with many subdirectories as follows.
http://mysite.com/dir1/subdir1/
http://mysite.com/dir1/subdir2/
http://mysite.com/dir2/
http://mysite.com/dir3/subdir1/
etc...
On each of my sites pages I need to extract the URL to the page minus the pagename and querystring.
So if the page name was http://mysite.com/dir1/subdir2/mypage.aspx?param=5&param2=9
I would need the following http://mysite.com/dir1/subdir2/ I cant find any properties of the httprequest object that make this URL format readily available.
Take a look at this. It should give you everything you need, especially Url.Segments.
This works as well:
System.IO.Path.GetDirectoryName(url).Replace(#"\","/");
You're right, such thing is not ready so you need to make it yourself. One recipe is:
public string GetSubFolderURL()
{
string url = "http";
if (string.Equals(Request.ServerVariables["HTTPS"], "ON", StringComparison.CurrentCultureIgnoreCase))
url += "s";
url += "://";
url += Request.ServerVariables["SERVER_NAME"];
int port;
if (Int32.TryParse(Request.ServerVariables["SERVER_PORT"], out port) && port != 80)
url += ":" + port;
url += Request.ServerVariables["SCRIPT_NAME"];
return url.Substring(0, url.LastIndexOf("/") + 1);
}

SEO: Duplicated URLs with and without dash "/" and ASP.NET MVC

after reading this article "Slash or not to slash" (link: http://googlewebmastercentral.blogspot.com/2010/04/to-slash-or-not-to-slash.html) on Google Webmaster Central Blog (the oficial one) I decided to test my ASP.NET MVC app.
For example:
http://domain.com/products and http://domain.com/products/ (with "/" in the end), return the code 200, which means: Google understands it as two different links and likely to be a "duplicated content". They suggest to choose the way you want... with or without dash and create a 301 permanent redirect to the preferred way.
So if I choose without dash, when I try to access http://domain.com/products/ it will return a 301 to the link without dash: http://domain.com/products.
The question is, how can I do that with ASP.NET MVC?
Thanks,
Gui
If your using IIS 7 you could use the URL Rewrite Extension ScottGu has a blog post about it here.
Alternatively if you want to do it in code you could inherit from PerRequestTask. Here some sample code the removes the www from an address - this is from Shrinkr:
public class RemoveWww : PerRequestTask
{
protected override TaskContinuation ExecuteCore(PerRequestExecutionContext executionContext)
{
const string Prefix = "http://www.";
Check.Argument.IsNotNull(executionContext, "executionContext");
HttpContextBase httpContext = executionContext.HttpContext;
string url = httpContext.Request.Url.ToString();
bool startsWith3W = url.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase);
bool shouldContinue = true;
if (startsWith3W)
{
string newUrl = "http://" + url.Substring(Prefix.Length);
HttpResponseBase response = httpContext.Response;
response.StatusCode = (int) HttpStatusCode.MovedPermanently;
response.Status = "301 Moved Permanently";
response.RedirectLocation = newUrl;
response.SuppressContent = true;
response.End();
shouldContinue = false;
}
return shouldContinue ? TaskContinuation.Continue : TaskContinuation.Break;
}
}
You would just need to check for the url ending with a / in your code.
** Note this does use a 3rd party dll - System.Web.MVC.Extensibility namespace. **
It dosnt matter really for Google, but what does matter is if both urls'
http://domain.com/products and http://domain.com/products/ show the same page, you also need to watch with windows servers that links to your site like from external pages where the user has typed http://domain.com/PRODUCTS/ will aloso be seen as a diffrent page as the web is case sensitive.
There is away round this with the use of canonical url meta tag, it tell s google what the page name is really, so will avoid duplicate pages which ant really diuplicate
http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html
you need to check the URI in the INIT event and check the URI to see if it coming in with the slash, if it is, simply do a redirect and add the 301 header to the output response.

Find application root URL without using ~

I need to construct the URL of a page in a String, to send it an email (as part of an email verification system). If i use the ~ symbol to denote the app root, it is taken literally.
The app will be deployed on a server on three different sites (on different ports) and each site can be accessed via 2 different URLs (one for LAn and one for internet).
So hardcoding the URL is out of question. I want to construct the url to verify.aspx in my application
Please help
You need this:
HttpContext.Current.Request.ApplicationPath
It's equivalent to "~" in a URL.
http://msdn.microsoft.com/en-us/library/system.web.httprequest.applicationpath.aspx
Unfortunately none of the methods listed generated the full url starting from http://---.
So i had to extract these from request.url. Something like this
Uri url=HttpContext.Current.Request.Url;
StringBuilder urlString = new StringBuilder();
urlString.Append(url.Scheme);
urlString.Append("://");
urlString.Append(url.Authority);
urlString.Append("/MyDesiredPath");
Can someone spot any potential problems with this?
Try:
HttpRequest req = HttpContext.Current.Request;
string url = req.Url.GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped)
+ ((req.ApplicationPath.Length > 1) ? req.ApplicationPath : "");
You need to put the URL as part of your web application's configuration. The web application does not know how it can be reached from the outside world.
E.g. consider a scenario where there's multiple proxies and load balancers in front of your web server... how would the web server know anything but its own IP?
So, you need to configure each instance of your web application by adding the base URL e.g. as an app setting in its web.config.
You can use HttpRequest.RawURL (docs here)property and base your URL on that, but if you are behind any kind of redirection, the RawURL may not reflect the actual URL of your application.
I ended up with this. I take the request url, and use the position of Request.ApplicationRoot to discover the left part of the uri. Should work with applications hosted in a virtual directory "/example" or in the root "/".
private string GetFullUrl(string relativeUrl)
{
if (string.IsNullOrWhiteSpace(relativeUrl))
throw new ArgumentNullException("relativeUrl");
if (!relativeUrl.StartsWith("/"))
throw new ArgumentException("url should start with /", "relativeUrl");
string current = Request.Url.ToString();
string applicationPath = Request.ApplicationPath;
int applicationPathIndex = current.IndexOf(applicationPath, 10, StringComparison.InvariantCultureIgnoreCase);
// should not be possible
if (applicationPathIndex == -1) throw new InvalidOperationException("Unable to derive root path");
string basePath = current.Substring(0, applicationPathIndex);
string fullRoot = string.Concat(
basePath,
(applicationPath == "/") ? string.Empty : applicationPath,
relativeUrl);
return fullRoot;
}
This has always worked for me:
string root = Request.Url.AbsoluteUri.Replace(Request.Url.PathAndQuery, "");

Resources