Tag Archives: Serialisation

Asp.Net MVC Code

How to POST JSON data to an Asp.Net MVC 2 app with ValidateAntiForgeryToken

The title says it all really, earlier this week I was attempting to submit data in the form of JSON to a controller method that implemented the ValidateAntiForgeryTokenAttribute.

On the client side of things  I managed to get the validation input generated by Html.AntiForgeryToken into my JSON object but I just kept on getting the “A required anti-forgery token was not supplied or was invalid” exception.

After a bit of research I found the problem to lie in the Asp.Net MVC 2 source, if you look at the ValidateAntiForgeryTokenAttribute file specifically on line 56 (OnAuthorization method) you’ll see this:

string formValue = filterContext.HttpContext.Request.Form[fieldName];

The validation class always checks the form for the token but we don’t have any form data when we post JSON!

So my solution was to modify like so:

      string value;
      if (filterContext.HttpContext.Request.ContentType.ToLower().Contains("json"))
      {
        var bytes = new byte[filterContext.HttpContext.Request.InputStream.Length];
        filterContext.HttpContext.Request.InputStream.Read(bytes, 0, bytes.Length);
        filterContext.HttpContext.Request.InputStream.Position = 0;
        var json = Encoding.ASCII.GetString(bytes);
        var jsonObject = JObject.Parse(json);
        value = (string)jsonObject[fieldName];
      }
      else
      {
        value = filterContext.HttpContext.Request.Form[fieldName];
      }

What happens here is I do a check to see if the post is json, if so I then pull the string from the input stream and using a JSON parser extract the token.

Since we’re modifying the ValidateAntiForgery class we might as allow it to work  with (or restrict to) any http verb.

This is what I’ve ended up with (sorry about the formatting):

namespace Your.App
{
  using System;
  using System.Linq;
  using System.Text;
  using System.Web;
  using System.Web.Mvc;
  using Newtonsoft.Json.Linq;

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
  public class ValidateJsonAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
  {
    private string _salt;
    private AntiForgeryDataSerializer _serializer;
    private readonly AcceptVerbsAttribute _verbs;

    public string Salt
    {
      get
      {
        return _salt ?? String.Empty;
      }
      set
      {
        _salt = value;
      }
    }

    internal AntiForgeryDataSerializer Serializer
    {
      get
      {
        if (_serializer == null)
        {
          _serializer = new AntiForgeryDataSerializer();
        }
        return _serializer;
      }
      set
      {
        _serializer = value;
      }
    }

    public ValidateJsonAntiForgeryTokenAttribute(HttpVerbs verbs = HttpVerbs.Post):this(null, verbs)
    {
    }

    public ValidateJsonAntiForgeryTokenAttribute(string salt, HttpVerbs verbs = HttpVerbs.Post)
    {
      this._verbs = new AcceptVerbsAttribute(verbs);
      this._salt = salt;
    }

    private bool ValidateFormToken(AntiForgeryData token)
    {
      return (String.Equals(Salt, token.Salt, StringComparison.Ordinal));
    }

    private static HttpAntiForgeryException CreateValidationException()
    {
      return new HttpAntiForgeryException("A required anti-forgery token was not supplied or was invalid.");
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
      if (filterContext == null)
      {
        throw new ArgumentNullException("filterContext");
      }

      // We only need to validate this if it's a post
      string httpMethodOverride = filterContext.HttpContext.Request.GetHttpMethodOverride();
      if (!this._verbs.Verbs.Contains(httpMethodOverride, StringComparer.OrdinalIgnoreCase))
      {
        return;
      }

      string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null);
      string cookieName = AntiForgeryData.GetAntiForgeryTokenName(filterContext.HttpContext.Request.ApplicationPath);

      HttpCookie cookie = filterContext.HttpContext.Request.Cookies[cookieName];
      if (cookie == null || String.IsNullOrEmpty(cookie.Value))
      {
        // error: cookie token is missing
        throw CreateValidationException();
      }
      AntiForgeryData cookieToken = Serializer.Deserialize(cookie.Value);

      string value;
      if (filterContext.HttpContext.Request.ContentType.ToLower().Contains("json"))
      {
        var bytes = new byte[filterContext.HttpContext.Request.InputStream.Length];
        filterContext.HttpContext.Request.InputStream.Read(bytes, 0, bytes.Length);
        filterContext.HttpContext.Request.InputStream.Position = 0;
        var json = Encoding.ASCII.GetString(bytes);
        var jsonObject = JObject.Parse(json);
        value = (string)jsonObject[fieldName];
      }
      else
      {
        value = filterContext.HttpContext.Request.Form[fieldName];
      }

      if (String.IsNullOrEmpty(value))
      {
        // error: form token is missing
        throw CreateValidationException();
      }
      AntiForgeryData formToken = Serializer.Deserialize(value);

      if (!String.Equals(cookieToken.Value, formToken.Value, StringComparison.Ordinal))
      {
        // error: form token does not match cookie token
        throw CreateValidationException();
      }

      if (!ValidateFormToken(formToken))
      {
        // error: custom validation failed
        throw CreateValidationException();
      }
    }
  }
}

Also I had to copy over 2 classes from the MVC source. The “AntiForgeryData” and “AntiForgeryDataSerializer” since they are interal and my project wouldn’t compile without them.

Final note, if you’re having trouble with the Serializer you copied over (in the Deserialize method and casting to a Triplet) then this may be of some help (.Net 4.0 only):

                dynamic deserializedObj = formatter.Deserialize(serializedToken);
                return new AntiForgeryData() {
                    Salt = deserializedObj[0],
                    Value = deserializedObj[1],
                    CreationDate = (DateTime)deserializedObj[2]
                };
Code

Short Break

It’s been sometime since I’ve last posted an update to my blog so I thought I’d better get in gear and do something about it. So for now here is a reminder for those who use Memcached with Enyim.

USE THE [Serializable] ATTRIBUTE ON YOUR CLASSES.

I keep forgetting that.

Code

Serialising / Deserialising objects to Xml in .Net

Serialising / Deserialising objects to Xml: from my StackOverFlow

private static readonly Dictionary<Type, XmlSerializer> serialisers = new Dictionary<Type, XmlSerializer>();

/// <summary>Serialises an object of type T in to an xml string</summary>
/// <typeparam name="T">Any class type</typeparam>
/// <param name="objectToSerialise">Object to serialise</param>
/// <returns>A string that represents Xml, empty oterwise</returns>
public static string XmlSerialise<T>(this T objectToSerialise) where T : class, new()
{
  XmlSerializer serialiser;

  var type = typeof(T);
  if (!serialisers.ContainsKey(type))
  {
    serialiser = new XmlSerializer(type);
    serialisers.Add(type, serialiser);
  }
  else
  {
    serialiser = serialisers[type];
  }

  string xml;
  using (var writer = new StringWriter())
  {
    serialiser.Serialize(writer, objectToSerialise);
    xml = writer.ToString();
  }

  return xml;
}

/// <summary>Deserialises an xml string in to an object of Type T</summary>
/// <typeparam name="T">Any class type</typeparam>
/// <param name="xml">Xml as string to deserialise from</param>
/// <returns>A new object of type T is successful, null if failed</returns>
public static T XmlDeserialise<T>(this string xml) where T : class, new()
{
  XmlSerializer serialiser;

  var type = typeof(T);
  if (!serialisers.ContainsKey(type))
  {
    serialiser = new XmlSerializer(type);
    serialisers.Add(type, serialiser);
  }
  else
  {
    serialiser = serialisers[type];
  }

  T newObject;

  using (var reader = new StringReader(xml))
  {
    try { newObject = (T)serialiser.Deserialize(reader); }
    catch { return null; } // Could not be deserialized to this type.
  }

  return newObject;
}

When building the serialisation I had a help from an online example – but cannot remeber where it is…