Validate Anti-Forgery in ASP.NET MVC- The Automated Way






4.73/5 (7 votes)
Validate Anti-Forgery in 6 easy steps
Introduction
While trying to secure our ASP.NET MVC Web applications with recommended stuff like [ValidateAntiForgeryToken]
filter to be protected from CSRF attack.
Reference: OWASP – Cross-Site Request Forgery (CSRF)
I found that when we apply the traditional way, it’s more likely to forget these stuff somewhere, if it’s not you, it will be your teammates.
Traditional Way
// Action
[ValidateAntiForgeryToken]
public ActionResult AddUser(string userName)
{
return View();
}
// View
@using(Html.BeginForm("AddUser", "Home"))
{
@Html.AntiForgeryToken()
@Html.TextBox("userName")
<button type="submit">Save</button>
}
So why we don’t we do this stuff automatically for all Post
requests as recommended.
Automated Way
By applying these 6 easy steps, we will be able to protect our web applications from Cross-Site Request Forgery (CSRF).
Back End Work
- Security Filter Provider: This filter provider will apply
ValidateAntiForgeryToken
filter attribute on allPost
requests:public class SecurityFilterProvider : IFilterProvider { public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { List<Filter> filterSet = new List<Filter>(); // ValidateAntiForgeryToken for POST requests + // Skip Actions with UnValidateAntiForgeryToken string verb = controllerContext.HttpContext.Request.HttpMethod; if (String.Equals(verb, "POST", StringComparison.OrdinalIgnoreCase) && !actionDescriptor.IsDefined(typeof(UnValidateAntiForgeryToken), true)) { filterSet.Add(new Filter(new ValidateAntiForgeryTokenAttribute(), FilterScope.Global, null)); } return filterSet; } }
- Register Filter Provider in Global.asax:
protected void Application_Start() { ... FilterProviders.Providers.Add(new SecurityFilterProvider()); ... }
- This filter skips
Action
from being validated automatically:public class UnValidateAntiForgeryToken : ActionFilterAttribute { // This Filter has no use except skipping AntiForgeryToken Attribute from taking place } [HttpPost] [UnValidateAntiForgeryToken] public ActionResult AddUser(string userName) { ... }
Front End Work
- Render
AntiForgeryToken
input variable, this should be inLayout
orView
.var antoForgeryToken = '@Html.AntiForgeryToken()';
- This will post
AntiForgeryToken
with forms:// Append AntiForgeryTiken Input to Form $('form').submit(function (event) { if ($(this).attr("method").toUpperCase() == "POST" && !$(this).find("[name=" + $(antiForgeryToken).attr("name") + "]").length) { $(this).append($(antiForgeryToken)); } });
- This will post
AntiForgeryToken
with Jquery-Ajax, and Ajax Action Links:// Append AntiForgeryToken to Jquery Ajax Requests$.param() $.ajaxPrefilter(function (options, originalOptions, jqXHR) { if (options.type.toUpperCase() == "POST") { if (!originalOptions.data.__RequestVerificationToken) { var token = { __RequestVerificationToken: $(antiForgeryToken).val() }; var data = $.isArray(originalOptions.data) ? originalOptions.data[0] : originalOptions.data; $.extend(data, token); options.data = $.param(data); } } });
Recommended
We can use @Html.BeginSecureForm
instead of BeginForm
, and @Ajax.BeginSecureForm
.
This Overload
methods adds AntiForgery
Token to the form:
// View
@using (Html.BeginSecureForm("AddUser", "Home"))
{
@Html.TextBox("userName")
<button type="submit">Save</button>
}
// BeginForm replacement for HtmlHelper
public static MvcForm BeginSecureForm
(this HtmlHelper htmlHelper, string actionName, string controllerName)
{
var form = htmlHelper.BeginForm(actionName, controllerName);
htmlHelper.ViewContext.Writer.Write(htmlHelper.AntiForgeryToken().ToHtmlString());
return form;
}
// BeginForm replacement for AjaxHelper
public static MvcForm BeginSecureForm(this AjaxHelper ajaxHelper, AjaxOptions ajaxOptions)
{
var form = ajaxHelper.BeginForm(ajaxOptions);
ajaxHelper.ViewContext.Writer.Write(AntiForgery.GetHtml());
return form;
}
All overloading methods of BeginSecureForm
is found at SecureFormExtensions.cs and SecureAjaxExtensions.cs.