AngularJS has built-in support for XSRF (AKA anti forgery)
XSRF is a technique by which an unauthorized site can gain your user's
private data. Angular provides a mechanism to counter XSRF. When
performing XHR requests, the $http service reads a token from a cookie
(by default, XSRF-TOKEN) and sets it as an HTTP header (X-XSRF-TOKEN).
Since only JavaScript that runs on your domain could read the cookie,
your server can be assured that the XHR came from JavaScript running
on your domain. The header will not be set for cross-domain requests.
If you want to take advantage of this functionality, you have to create action filters that will create and verify the XSRF-TOKEN cookie.
Here is a simplified version of what I use.
AntiForgeryTokenCookieAttribute - use this attribute to add the XSRF-TOKEN cookie
/// <summary>
/// Create a XSRF token in the XSRF-TOKEN cookie which is automatically read by AngularJS
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AntiForgeryTokenCookieAttribute : ActionFilterAttribute
{
private readonly IAntiForgeryVerificationTokenStore _verificationTokenStore = new AntiForgeryVerificationTokenCookieStore(); //TODO: make configurable
private const string CookieName = "XSRF-TOKEN"; //TODO: make configurable
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
var oldVerificationToken = _verificationTokenStore.GetVerificationToken(filterContext.HttpContext.Request);
string newVerificationToken;
string newToken;
AntiForgery.GetTokens(oldVerificationToken, out newVerificationToken, out newToken);
if (newVerificationToken != null)
{
_verificationTokenStore.StoreVerificationToken(filterContext.HttpContext.Response, newVerificationToken);
}
filterContext.HttpContext.Response.Cookies.Add(new HttpCookie(CookieName, newToken));
}
}
ValidateAntiForgeryTokenHeaderAttribute
/// <summary>
/// Validate the XSRF token stored in the X-XSRF-TOKEN header.
/// If the header doesn't exist, look for the XSRF token in the from post.
///
/// Compatible with ValidateAntiForgeryTokenAttribute
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateAntiForgeryTokenHeaderAttribute : FilterAttribute, IAuthorizationFilter
{
private readonly IAntiForgeryVerificationTokenStore _verificationTokenStore = new AntiForgeryVerificationTokenCookieStore(); //TODO: make configurable
private const string TokenHeaderName = "X-XSRF-TOKEN"; //TODO: make configurable
public void OnAuthorization(AuthorizationContext filterContext)
{
var token = filterContext.HttpContext.Request.Headers[TokenHeaderName];
if (token != null)
{
//validate the token stored in header
var verificationToken = _verificationTokenStore.GetVerificationToken(filterContext.HttpContext.Request);
if (verificationToken == null) { throw new HttpAntiForgeryException("Required verification token not found"); }
AntiForgery.Validate(verificationToken, token);
}
else
{
//validate the token stored in form. Same as ValidateAntiForgeryTokenAttribute
AntiForgery.Validate();
}
}
}
IAntiForgeryVerificationTokenStore
public interface IAntiForgeryVerificationTokenStore
{
string GetVerificationToken(HttpRequestBase request);
void StoreVerificationToken(HttpResponseBase response, string token);
}
AntiForgeryVerificationTokenCookieStore
public class AntiForgeryVerificationTokenCookieStore : IAntiForgeryVerificationTokenStore
{
public string GetVerificationToken(HttpRequestBase request)
{
if (request == null) { throw new ArgumentNullException("request"); }
var token = request.Cookies[AntiForgeryConfig.CookieName];
return token != null ? token.Value : null;
}
public void StoreVerificationToken(HttpResponseBase response, string token)
{
if (response == null) { throw new ArgumentNullException("response"); }
if (token == null) { throw new ArgumentNullException("token"); }
response.Cookies.Add(new HttpCookie(AntiForgeryConfig.CookieName, token) { HttpOnly = true, Secure = AntiForgeryConfig.RequireSsl });
}
}