CSRF attacks are one of the many security issues that web developers must defend against. Fortunately, ASP.Net MVC makes it easy to defend against CSRF attacks. Simply slap on [ValidateAntiForgeryToken] to every POST action and include @Html.AntiForgeryToken() in every form, and your forms will be secure against CSRF.
However, it is easy to forget to apply [ValidateAntiForgeryToken] to every action. To prevent such mistakes, you can create a unit test that loops through all of your controller actions and makes sure that every [HttpPost] action also has [ValidateAntiForgeryToken].
Since there may be some POST actions that should not be protected against CSRF, you’ll probably also want a marker attribute to tell the test to ignore some actions.
This can be implemented like this:
First, define the marker attribute in the MVC web project. This attribute can be applied to a single action, or to a controller to allow every action in the controller.
///<summary>Indicates that an action or controller deliberately /// allows CSRF attacks.</summary> ///<remarks>All [HttpPost] actions must have /// [ValidateAntiForgeryToken]; any deliberately unprotected /// actions must be marked with this attribute. /// This rule is enforced by a unit test.</remarks> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public sealed class AllowCsrfAttacksAttribute : Attribute { }
Then, add the following unit test:
[TestMethod] public void CheckForCsrfProtection() { var controllers = typeof(MvcApplication).Assembly.GetTypes().Where(typeof(IController).IsAssignableFrom); foreach (var type in controllers.Where(t => !t.IsDefined(typeof(AllowCsrfAttacksAttribute), true))) { var postActions = type.GetMethods() .Where(m => !m.ContainsGenericParameters) .Where(m => !m.IsDefined(typeof(ChildActionOnlyAttribute), true)) .Where(m => !m.IsDefined(typeof(NonActionAttribute), true)) .Where(m => !m.GetParameters().Any(p => p.IsOut || p.ParameterType.IsByRef)) .Where(m => m.IsDefined(typeof(HttpPostAttribute), true)); foreach (var action in postActions) { //CSRF XOR AntiForgery Assert.IsTrue(action.IsDefined(typeof(AllowCsrfAttacksAttribute), true) != action.IsDefined(typeof(ValidateAntiForgeryTokenAttribute), true), action.Name + " is [HttpPost] but not [ValidateAntiForgeryToken]"); } } }typeof(MvcApplication) must be any type in the assembly that contains your controllers. If your controllers are defined in multiple assemblies, you’ll need to include those assemblies too.