Julian Jelfs’ Blog

LESS (or SASS) can be MORE

Posted in Uncategorized by julianjelfs on April 27, 2013

When converting a conventional CSS file to SASS the other day it occurred to me that it could actually encourage less efficient CSS by creating the illusion of reuse. This seems specifically to be true when using mixins. Suppose I have several different kinds of icon and they just differ by background image, then I would naturally consider using a mixin to accomplish that like this:

@mixin icon($img) {
    background-image: url("#{$img}");
    background-repeat:no-repeat;
    background-position:center center;
    height:20px;
}

.error-icon
{
    @include icon("/images/error.png");
}

.save-icon
{
    @include icon("/images/save.png");
}

This gives me the illusion that I have reused that section of CSS. But really when you look at the output CSS you can see that the content of the mixin is duplicated:

.error-icon
{
    background-image: url("/images/error.png");
    background-repeat:no-repeat;
    background-position:center center;
    height:20px;
}

.save-icon
{
    background-image: url("/images/save.png");
    background-repeat:no-repeat;
    background-position:center center;
    height:20px;
}

In fact it is more efficient to create a separate CSS class for the common attributes and one for the icon specific background image like this:

.icon 
{
    background-repeat:no-repeat;
    background-position:center center;
    height:20px;
}

.error-icon
{
    background-image: url("/images/error.png");
}

.save-icon
{
    background-image: url("/images/save.png");
}

The moral of the story is not to forget that the important thing is is what actually gets generated, not just how cool your pre-processed file looks.

Tagged with: , ,

Securely posting application/json data to Asp.Net MVC4

Posted in Javascript, MVC by julianjelfs on April 3, 2013

Asp.Net MVC4 has some good mechanisms to protect against cross-site request forgery. The gist is that on the server you generate a unique token that is written out as a cookie and then also written out to the page you are rendering. Then when the system receives data via a POST request, it can check that the anti-forgery token has been supplied in both the submitted form and in the cookie and that they match. This way, if the user has been tricked into submitting a form that did not originate on the server as the user supposes, that request may have assumed the correct cookie, but will not have the matching form field counterpart and the request will fail.

We do this in Asp.Net MVC4 by simply calling the @Html.AntiForgeryToken() helper inside our view and then adorning our controller’s POST actions with the ValidateAntiForgeryToken attribute. Easy. This works really well if you are taking a traditional approach and posting forms of data to the server. But it is now very common for people to be using some kind of client side MVC framework (angular, knockout, ember etc etc) or even just using $.post to send json data back to the server outside a form submission.

I have seen a lot of people asking the question, how come my ValidateAntiForgeryToken doesn’t work when I post data back to the server using an ajax call in this way? Usually the response is that they are using the wrong content type. They are using “application/json” and they should be using “application/x-www-form-urlencoded” and this is why the anti forgery token cannot be read. This is true – the ValidateAntiForgeryToken attribute specifically looks in the Request.Form for the __RequestVerificationToken value and so it will only work with form url encoded data. But the answer is not to change the content type of your request it is to change your attribute.

The content type of “application/json” is entirely appropriate and correct. That is what you are doing – posting json. It’s much more easily parsed and it’s easier to read across the wire using something like fiddler, it’s easy to serialise and deserialise, it works with MVC model binding out of the box, so leave it alone. All you need to do is tag the verification token onto your request somehow and write a new attribute to pick it out of the request according to your rules. For example, it can easily be stored in and retrieved from a custom http header.

Continue to write the token out to the page using @Html.AntiForgeryToken() and replace the ValidateAntiForgeryToken attribute with something like this:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateJsonAntiForgeryToken : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var request = filterContext.HttpContext.Request;

if (request.HttpMethod == WebRequestMethods.Http.Post)
{
if (request.IsAjaxRequest())
AntiForgery.Validate(CookieValue(request), request.Headers["__RequestVerificationToken"]);
else
new ValidateAntiForgeryTokenAttribute().OnAuthorization(filterContext);
}
}

private string CookieValue(HttpRequestBase request)
{
var cookie = request.Cookies[AntiForgeryConfig.CookieName];
return cookie != null ? cookie.Value : null;
}
}

Then modify your client code to supply the token as a custom http header. In my case, I have created an angular service that wraps $http.post to achieve this as follows:

 

app.factory("xsrfPost", function($http) {
return {
post: function(url, args) {
var config = {
headers: {
'__RequestVerificationToken' : angular.element("input[name='__RequestVerificationToken']").val()
}
};
return $http.post(url, args, config);
}
};
})

Now, you can use a content-type that makes sense and still take advantage of the framework support for anti-XSRF protection.