Skip to content

Instantly share code, notes, and snippets.

@kamranayub
Created April 24, 2013 05:12
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kamranayub/5449779 to your computer and use it in GitHub Desktop.
Save kamranayub/5449779 to your computer and use it in GitHub Desktop.
A working ASP.NET Web API MVC 4 Anti-Forgery approach that also works on cloud hosts like AppHarbor. See: http://kamranicus.com/Blog/Posts/70/protip-using-anti-forgery-token-with-aspnet-web-ap
@* Include anywhere within <body> *@
@Html.AntiForgeryToken()
var options = {};
// jQuery options
// options.url = foo;
// CSRF Token
var csrfToken = $("input[name='__RequestVerificationToken']").val();
if (csrfToken) {
options.headers = {
"X-XSRF-Token": csrfToken
};
}
// Use jQuery as usual
return $.ajax(options);
[ValidateHttpAntiForgeryToken]
public HttpResponseMessage PostSomething(Something something) {
// ... whatever
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Helpers;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Common.Logging;
namespace Web.Attributes {
/// <summary>
/// Validates Anti-Forgery CSRF tokens for Web API
/// </summary>
/// <remarks>
/// See MVC 4 SPA template
/// </remarks>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class ValidateHttpAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter {
public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) {
HttpRequestMessage request = actionContext.Request;
try {
if (IsAjaxRequest(request)) {
ValidateRequestHeader(request);
} else {
AntiForgery.Validate();
}
} catch (Exception ex) {
LogManager.GetCurrentClassLogger().Warn("Anti-XSRF Validation Failed", ex);
actionContext.Response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.Forbidden,
RequestMessage = actionContext.ControllerContext.Request
};
return FromResult(actionContext.Response);
}
return continuation();
}
private Task<HttpResponseMessage> FromResult(HttpResponseMessage result) {
var source = new TaskCompletionSource<HttpResponseMessage>();
source.SetResult(result);
return source.Task;
}
private bool IsAjaxRequest(HttpRequestMessage request) {
IEnumerable<string> xRequestedWithHeaders;
if (request.Headers.TryGetValues("X-Requested-With", out xRequestedWithHeaders)) {
string headerValue = xRequestedWithHeaders.FirstOrDefault();
if (!String.IsNullOrEmpty(headerValue)) {
return String.Equals(headerValue, "XMLHttpRequest", StringComparison.OrdinalIgnoreCase);
}
}
return false;
}
private void ValidateRequestHeader(HttpRequestMessage request) {
var headers = request.Headers;
var cookie = headers
.GetCookies()
.Select(c => c[AntiForgeryConfig.CookieName])
.FirstOrDefault();
IEnumerable<string> xXsrfHeaders;
if (headers.TryGetValues("X-XSRF-Token", out xXsrfHeaders)) {
var rvt = xXsrfHeaders.FirstOrDefault();
if (cookie == null) {
throw new InvalidOperationException(String.Format("Missing {0} cookie", AntiForgeryConfig.CookieName));
}
AntiForgery.Validate(cookie.Value, rvt);
} else {
var headerBuilder = new StringBuilder();
headerBuilder.AppendLine("Missing X-XSRF-Token HTTP header:");
foreach (var header in headers) {
headerBuilder.AppendFormat("- [{0}] = {1}", header.Key, header.Value);
headerBuilder.AppendLine();
}
throw new InvalidOperationException(headerBuilder.ToString());
}
}
}
}
<!-- Ensure you set Machine Key for a web farm environment (cloud host) -->
<!-- Use this to generate a key: http://aspnetresources.com/tools/machineKey -->
<!-- For more info see: http://stackoverflow.com/questions/1360078/asp-net-mvc-validation-of-viewstate-mac-failed -->
<system.web>
<machineKey
validationKey="XXX"
decryptionKey="XXX" />
</system.web>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment