Created
April 24, 2013 05:12
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@* Include anywhere within <body> *@ | |
@Html.AntiForgeryToken() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ValidateHttpAntiForgeryToken] | |
public HttpResponseMessage PostSomething(Something something) { | |
// ... whatever | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- 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