protecting against CSRF in asp.net web application when using ajax - asp.net

I have an asp web application I wanted to update to prevent cross site request forgery attacks.
I have used the Microsoft auto-generated code from VS 2012, and added it to the master page as described here. It is working well, but one page posts JSON via an AJAX request to a webmethod
I would like to check this ajax request as well.
The forseeable problems are:
var responseCookie = new HttpCookie(AntiXsrfTokenKey)
{
//Set the HttpOnly property to prevent the cookie from
//being accessed by client side script
HttpOnly = true,
this can obviously be changed, but this would then seem to increase site vulnerability. Is this a significant issue?
I can send the value of the viewstate hidden input with the ajax request, but this will then need to be decoded back into key value pairs to do the equivalent of:
(string)ViewState[AntiXsrfTokenKey] != _antiXsrfTokenValue
Is there an easy way to use existing asp.net methods to do this?
Thank you for any help.

Here is what i have discovered. I ended up using the LosFormatter, as described by geedubb, by adding the following code to the MasterPage, and assigning the value to a hidden input which is posted back with the ajax request. I did not realise when I posted the question that HttpCookie.HttpOnly property still posts back the cookie on an ajax request, and so can be left set to false.
internal string GetToken()
{
// call the static method to guarantee LosFormatter remains threadsafe
return GetToken(_antiXsrfTokenValue);
}
private static string GetCurrentUserName()
{
var currentUser = HttpContext.Current.User.Identity;
return (currentUser == null) ? string.Empty : currentUser.Name;
}
private static string GetToken(string token)
{
var los = new System.Web.UI.LosFormatter(true, token);
var writer = new System.IO.StringWriter();
var data = new Dictionary<string,string>();
data.Add("TokenValue",token);
data.Add("UserNameKey", GetCurrentUserName());
los.Serialize(writer, data);
return writer.ToString();
}
internal static void Validate(string token)
{
var request = HttpContext.Current.Request;
var requestCookie = request.Cookies[AntiXsrfTokenKey];
var antiXsrfTokenValue = requestCookie.Value;
var los = new System.Web.UI.LosFormatter(true, antiXsrfTokenValue);
var xsrfData = (Dictionary<string,string>)los.Deserialize(token);
if (xsrfData["TokenValue"] != antiXsrfTokenValue || xsrfData["UserNameKey"] != GetCurrentUserName())
{
throw new System.Security.Authentication.AuthenticationException("Validation of Anti-XSRF token failed.");
}
}
Initially, I had tried sending the value of the _VIEWSTATE hidden input, using the same code
var los = new System.Web.UI.LosFormatter(true, antiXsrfTokenValue);
var ajaxViewState = los.Deserialize(token)
but this threw an error stating the supplied key could not deserialze the string. obviously setting
Page.ViewStateUserKey = _antiXsrfTokenValue;
has a more complex key than the supplied key alone. I would be interested if anyone knew how to deserialize a viewstate string with a userKey.
The only problem with the method I have provided is the size of the string posted back - 1976 characters long for a GUID + 6 character username!!!!
If approaching this problem again, I would reference the System.Web.WebPages.dll (used in an mvc project), and use the same methods which create the Html.AntiForgeryToken in MVC
namespace System.Web.Helpers
{
/// <summary>
/// Provides access to the anti-forgery system, which provides protection against
/// Cross-site Request Forgery (XSRF, also called CSRF) attacks.
/// </summary>
public static class AntiForgery
{
public static void GetTokens(string oldCookieToken, out string newCookieToken, out string formToken)
public static void Validate()

Related

Deserialize viewstate string after Page.ViewStateUserKey is set

I am using the auto-generated code for preventing cross site forgery attacks with asp.net web applications - ie:
protected const string AntiXsrfTokenKey = "__AntiXsrfToken";
private string _antiXsrfTokenValue;
protected void Page_Init(object sender, EventArgs e)
{
var requestCookie = Request.Cookies[AntiXsrfTokenKey];
Guid requestCookieGuidValue;
if (requestCookie != null && Guid.TryParse(requestCookie.Value, out requestCookieGuidValue))
{
_antiXsrfTokenValue = requestCookie.Value;
Page.ViewStateUserKey = _antiXsrfTokenValue;
When using an Ajax/Webmethod request, I would also like to validate the request before altering the database, by posting back the value of the _VIEWSTATE hidden input.
However, when I try
internal static void Validate(string encodedViewstate)
{
var request = HttpContext.Current.Request;
var requestCookie = request.Cookies[AntiXsrfTokenKey];
var antiXsrfTokenValue = requestCookie.Value;
var los = new System.Web.UI.LosFormatter(true, antiXsrfTokenValue);
var xsrfData = los.Deserialize(encodedViewstate);
the los.Deserialize method fails with:
Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm
ie the UserKey alone is not the correct key for the encoded viewstate string.
Can anyone please help in how to deserialize the viewstate, encoded after setting the ViewStateUserKey property (ie some combination of the MAC and UserKey). Thanks for your thoughts/expertise.
You'll need to use the same PageStatePersister instance that the Page itself uses. Otherwise this check won't work reliably. For example, consider this instance method in your Page's code-behind:
private void CheckCsrfToken() {
var persister = this.PageStatePersister;
persister.Load();
if (persister.ViewState == null) {
throw new Exception("Validation failed.");
}
}
As long as Page.ViewStateUserKey is already set, the returned persister instance will have its modifier also set appropriately.

Using FormsAuthentication on webMethod

I have a page that has a comment section. This section communicates to a WebMethod in order to insert a new comment.
[WebMethod]
public static bool insertComment(string commentString)
{
//userName validation here
string userName = (FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name);
return new CommentClass().InsertComment(commentString, userName);
}
The problem is: "An object reference is required for the non-static field".
I know I could send the information from a hidden field, or a div, however, that information field may be changed easily.
So which way could be used to know which user is posting, in server side?
thanks a lot!
Request object is an instance that lives in Page, so you need a reference to access this object in a static context. You can use HttpContext.Current.Request for accessing the Request in this context.
[WebMethod]
public static bool insertComment(string commentString)
{
//userName validation here
string userName =
(FormsAuthentication.Decrypt(
HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name);
return new CommentClass().InsertComment(commentString, userName);
}

Problem with null object reference in Url.Action in MVC3 project

I am trying to set up a mocking scenario for my payment processor on a web site. Normally, my site redirects to the processor site, where the user pays. The processor then redirects back to my site, and I wait for an immediate payment notification (IPN) from the processor. The processor then posts to my NotifyUrl, which routes to the Notify action on my payments controller (PayFastController). To mock, I redirect to a local action, which after a conformation click, spawns a thread to post the IPN, as if posted by the processor, and redirects back to my registration process.
My mock processor controller uses the following two methods to simulate the processor's response:
[HttpGet]
public RedirectResult Pay(string returnUrl, string notifyUrl, int paymentId)
{
var waitThread = new Thread(Notify);
waitThread.Start(new { paymentId, ipnDelay = 1000 });
return new RedirectResult(returnUrl);
}
public void Notify(dynamic data)
{
// Simulate a delay before PayFast
Thread.Sleep(1000);
// Delegate URL determination to the model, vs. directly to the config.
var notifyUrl = new PayFastPaymentModel().NotifyUrl;
if (_payFastConfig.UseMock)
{
// Need an absoluate URL here just for the WebClient.
notifyUrl = Url.Action("Notify", "PayFast", new {data.paymentId}, "http");
}
// Use a canned IPN message.
Dictionary<string, string> dict = _payFastIntegration.GetMockIpn(data.paymentId);
var values = dict.ToNameValueCollection();
using (var wc = new WebClient())
{
// Just a reminder we are posting to Trocrates here, from PayFast.
wc.UploadValues(notifyUrl, "POST", values);
}
}
However, I get an 'Object reference not set to an instance of an object.' exception on the following line:
notifyUrl = Url.Action("Notify", "PayFast", new {data.paymentId}, "http");
data.paymentId has a valid value, e.g. 112, so I'm not passing any null references to the Url.Action method. I suspect I have lost some sort of context somewhere by calling Notify on a new thread. However, if I use just notifyUrl = Url.Action("Notify", "PayFast");, I avoid the exception, but I get a relative action URL, where I need the overload that takes a protocol parameter, as only that overload gives me the absolute URL that WebClient.UploadValues says it needs.
When you are inside the thread you no longer have access to the HttpContext and the Request property which the Url helper relies upon. So you should never use anything that relies on HttpContext inside threads.
You should pass all the information that's needed to the thread when calling it, like this:
waitThread.Start(new {
paymentId,
ipnDelay = 1000,
notifyUrl = Url.Action("Notify", "PayFast", new { paymentId }, "http")
});
and then inside the thread callback:
var notifyUrl = new PayFastPaymentModel().NotifyUrl;
if (_payFastConfig.UseMock)
{
// Need an absoluate URL here just for the WebClient.
notifyUrl = data.notifyUrl;
}

What is the best practice for updating a cookie that was set on a previous request in ASP.NET?

Here is the scenario. A cookie with the key "MyCookie" has been set on a previous request. I can access it via HttpContext.Request.Cookies.Get("MyCookie"). I want to perform an update such as adding another value to the Cookie Values collection, but I'm not 100% sure I am doing it right.
Am I doing this correctly in the following example?
public static void UpdateCookie(HttpContext context, string cookieName, Action<HttpCookie> updateCookie){
var cookie = context.Request.Cookies.Get(cookieName);
updateCookie(cookie);
context.Response.Cookies.Set(cookie);
}
To update a cookie, you need only to set the cookie again using the new values. Note that you must include all of the data you want to retain, as the new cookie will replace the previously set cookie. I'm going to assume that your implementation of updateCookie() does just that.
Otherwise, your general premise is correct. Here's an implementation I've used many times to do just that. (Note: _page is a reference to the current Page):
/// <summary>
/// Update the cookie, with expiration time a given amount of time from now.
/// </summary>
public void UpdateCookie(List<KeyValuePair<string, string>> cookieItems, TimeSpan? cookieLife)
{
HttpCookie cookie = _page.Request.Cookies[COOKIE_NAME] ?? new HttpCookie(COOKIE_NAME);
foreach (KeyValuePair<string, string> cookieItem in cookieItems)
{
cookie.Values[cookieItem.Key] = cookieItem.Value;
}
if (cookieLife.HasValue)
{
cookie.Expires = DateTime.Now.Add(cookieLife.Value);
}
_page.Response.Cookies.Set(cookie);
}

How can I read in C# XML from another page while logged in to the site

I have logged in to the site with my webbrowser and whenever I try to call
WebClient myWebClient = new WebClient();
string str = myWebClient.DownloadString("http://localhost/myxml.aspx");
Response.Write(str.ToString());
Or
XmlTextReader reader = new XmlTextReader(url);
while (reader.Read()) {
Response.Write(reader.ReadOuterXml());
}
Response.Write returns me the login page.
Is there away to attach user SessionId to WebClient or XmlTextReader or how can I request another page in C# with current logged user?
You'll need to use an object that can handle storing cookies. In this case, you'll need the HttpWebRequest class. You'll also need a CookieContainer to manage authentication cookies.
To do this you would:
Create a CookieContainer object (a cookie jar) that you can keep track of throughout the scope of every request you make.
Create an HttpWebRequest that logs into the site you're accessing.
Use the CookieContainer you created in step 1 for every subsequent request.
Below is an example of how to use the HttpWebRequest, HttpWebResponse, and CookieContainer classes together to make a simple request that will set some cookies, and then using those cookies on a subsequent request. The rest should be easy assuming everything is well formed markup ;)
CookieContainer cookieJar = new CookieContainer();
var webRequest = (HttpWebRequest)HttpWebRequest.Create("http://www.google.com");
webRequest.CookieContainer = cookieJar;
var webResponse = webRequest.GetResponse();
using (var reader = new StreamReader(webResponse.GetResponseStream()))
{
Response.Write(reader.ReadToEnd());
}
var anotherWebRequest = (HttpWebRequest)HttpWebRequest.Create("http://www.google.com/search?q=stackoverflow.com");
anotherWebRequest.CookieContainer = cookieJar;
webResponse = anotherWebRequest.GetResponse();
Another option (if you really want to use the WebClient class) would be to parse out the ResponseHeaders property of the class once you've made your request and include the appropriate cookies in your next request. This is a little more involved though since it requires you to manage your cookies manually.
Since I'm assuming that you want to be able to traverse your web responses as XML, I suggest you look into the open source library, HtmlAgilityPack. It allows you to send in markup from a web site that is (most likely) not well-formed, or has some sort of invalid markup in it, and then fixes the invalid parts so that you can traverse it like XML.
While doing some screen scraping, I had the same issue. I was requesting a Classic ASP app on an IIS server (I could tell by some of the headers that the server reponded with). The way I supported an ongoing session was by enabling Cookies on the WebClient. Theres no toggle for it, you have to subclass WebClient to get it to work.
public class CookieAwareWebClient : WebClient
{
protected CookieContainer _container = new CookieContainer();
public CookieContainer Cookies
{
get { return _container; }
set { _container = value; }
}
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest httpRequest = base.GetWebRequest(address) as HttpWebRequest;
if (httpRequest.CookieContainer != null)
{
if (httpRequest != null)
{
CookieCollection newCookies =
GetUniqueCookies(
address
,httpRequest.CookieContainer.GetCookies(address)
);
foreach (Cookie c in newCookies)
httpRequest.CookieContainer.Add(c);
}
}
else
httpRequest.CookieContainer = this.Cookies;
return (WebRequest)httpRequest;
}
Note: this isn't a unique solution, I found this out there on the web myself, but I've implemented the solution and it works really well.

Resources