How to Protect ASP.NET MVC Against Cross Site Attacks

Cross-Site Request Forgery (CSRF) attacks are common; they even affect some BigTech companies.

  • Netflix experienced CSRF vulnerabilities in 2006. Attackers could alter shipment addresses, login information, and DVD delivery addresses.
  • A CSRF attack on YouTube allowed an attacker to take control of the behavior of any user.
  • CSRF attackers who utilized ING Direct Banking’s web application to conduct unauthorized money transfers caused the company to lose money.
  • The flaw in McAfee Secure gave attackers access to modify the corporate network.

How did the assailants manage to do this? What methods did they employ, specifically? We must fully comprehend CSRF in order to comprehend this.

What is a Cross-Site Request Forgery (CSRF) attack?

CSRF occurs when an attacker sends illegal commands to a user who is already signed in on a website. Simply put, when you click on a malicious hyperlink, scripts that act on your behalf on the bank website where you are logged in are triggered. The attackers have your money, voila.

These are the characteristics of a malicious link.

  • “You are a winner.”
  • “You placed order # 648722.”

Why are CSRF flaws so important to patch in the ASP.NET MVC world?

Traditionally, ASP.NET MVC and ASP.NET Core have been among the most popular platforms for developing financial online applications, such as those for banks and hedge funds. These platforms are statistically more reliable than their competitors, such as Express or NodeJS, for financial online applications. Additionally, because to the superior tools and assistance offered, CSRF issues in ASP.NET Core are simpler to resolve than those in ASP.NET MVC. We’ll look through ways to address CSRF problems with ASP.NET MVC.

How to use a CSRF attack vector to assault an ASP.NET MVC website

We must first comprehend how CSRF problems originate in order to know how to resolve them. Consider logging onto your bank’s website at onlinebank.com. Additionally, a malicious website with this design is being visited in a separate tab. This is an example for spam email that you can find:

<h1>Affordable Medicare Plans are available. Hurry now!</h1>
<form action="http://onlinebank.com/transfer?account=76543865&amount=15000" method="post">
  <input type="submit" value="Get Started"/>
</form>

By pressing the F12 key on most browsers when you click the “Get Started” button, you can view the network traffic that is coming from it using the developer console that is built into your browser. The developer console is crucial for web development, and nearly all engineers who work on the front end browser code should be aware of it.

The session cookies would also be included in this traffic if you are already logged into the onlinebank.com website. The attacker now possesses all the necessary components to finish the puzzle:

  1. a logged-in user
  2. users’ session cookies
  3. a URL that steals the victims’ money.

It seems sense that CSRF attacks are also known as one-click and session-riding assaults.

Img tags are one of the additional ways the assault could occur.

After seeing how the assault could occur, let’s talk about our prevention measures.

Enter Anti-Forgery Tokens.

Using tokens that you would only know is advised as a defense against CSRF attacks. The tokens are generated by your ASP.NET MVC web project, and we check them on pertinent server requests. Use and verifying this token on POST, PUT, PATCH, and DELETE requests is optimal because GET requests are not allowed to change the information that has been persistent.

Let’s outline the steps needed.

  • A user accesses a page.
  • ASP.NET MVC creates two tokens in response to a page request. a cookie token and a token for a hidden form field. In the server’s response, both tokens are embedded.
  • Both of these tokens should be included in the request whenever the user performs an action that modifies data, such as submitting a form.
  • The server checks to see if the action request has both tokens; if not, it rejects the request.

In other words, imagine that you have the key to a bank locker and you want to open it, but you have to do it in front of a bank manager (using anti-forgery tokens given by the bank’s server) even though you have the locker key (a session cookie).

Show me the code for ASP.NET MVC.

You would add `@Html.AntiForgeryToken()` to your Html forms.

@using (Html.BeginForm("Transfer", "Account", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    <input type="submit" value="Submit Form" /></td>
}

It would translate to:

<form action="Account/Transfer" method="post">
  <input name="__RequestVerificationToken" type="hidden" value="CfDJles20ilG0VXAiNm3Fl5Lyu_JGpQDA......"/>    
  <input type="submit" value="Submit Form" />
</form>

As you can see, ASP.NET MVC has added `__RequestVerificationToken` to this form token as a hidden field. This token is generated at the server.

Now, when this form is submitted, the cookie token will be sent to the server along with the request, together with the form token and form data.

There is only one task remaining, which is to check both tokens at the server.

To do that, a method like `AntiForgery.Validate(cookieToken, formToken); ` will do the job. But for ASP.NET MVC, there is a built-in attribute that would do this job for you – `ValidateAntiForgeryToken`.

using System.Web.Mvc;
namespace Online.Bank.App.Controllers
{
    public class AccountController : Controller
    {
        [HttpPost]
        [ValidateAntiForgeryToken] // This attribute will do the Anti-Forgery token validation for you.
        public ActionResult Transfer()
        {
            return Json(true); // This line is added for brevity. You would be doing good stuff here.
        }
    }
}

What about AJAX and jQuery?

Instead of HTML form data, AJAX mostly uses JSON data. It presents a challenge for us. The code shown above is invalid. How can this be resolved?

Let’s imagine we need to use AJAX to call the endpoint Account/Manage.

We can ask the server to produce the tokens and provide them to us in this manner as the first step using razor syntax.

<script>
    @functions{
        public string GetAntiForgeryTokens()
        {
            string cookieToken, formToken;
            AntiForgery.GetTokens(null, out cookieToken, out formToken);
            return cookieToken + ":" + formToken;
        }
    }
</script>

And then send the tokens to the server using AJAX.

<input type="button" onclick="manageAccount()" value="Manage Account" />
<script>
    function manageAccount() {
        $.ajax("<baseurl>/Account/Manage", {
            type: "post",
            contentType: "application/json",
            data: {  }, // JSON data
            dataType: "json",
            headers: {
                '__RequestVerificationToken': '@GetAntiForgeryTokens()'
            },
            success: function (data) {
                console.log(`Account updated: ${data}`);
            }
        });
    }
</script>

Finally, verify these tokens on the server side. The `Manage` action would be similar to this.

using System;
using System.Linq;
using System.Web.Helpers;
using System.Web.Mvc;
namespace Online.Bank.App.Controllers
{
    public class AccountController : Controller
    {
        [HttpPost]
        public ActionResult Manage()
        {
            string cookieToken = "";
            string formToken = "";
            var tokenHeaders = Request.Headers.GetValues("__RequestVerificationToken");
            string[] tokens = tokenHeaders?.First()?.Split(':');
            if (tokens.Length == 2)
            {
                cookieToken = tokens[0].Trim();
                formToken = tokens[1].Trim();
            }
            try
            {
                AntiForgery.Validate(cookieToken, formToken);
            }
            catch (Exception ex)
            {
                // Alert folks that someone is trying to attack this method.
            }
            return Json(true); // This line is added for brevity. You would be doing good stuff here.
        }
    }
}

That’s it. Now our Account/Manage endpoint is protected.

But do you see a problem? Do we have to repeat this in every action method? How to avoid that?

Custom Anti-Forgery Attribute to the rescue!

What if we use the code above to create a unique ASP.NET MVC attribute? After that, we can add that attribute to action methods or controllers, correct?

using System;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;
namespace Online.Bank.App.Attributes
{
  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
  public sealed class ValidateHeaderAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
  {
    // Bonus method
    private void TellEveryoneWeBlockedAttacker(HttpContextBase httpContext, Exception ex)
    {
      string controllerName = httpContext?.Request?.RequestContext?.RouteData?.Values["controller"]?.ToString();
      string actionName = httpContext?.Request?.RequestContext?.RouteData?.Values["action"]?.ToString();
      ex.Data.Add("ControllerName", controllerName);
      ex.Data.Add("ActionName", actionName);
      // Use the exception we created to notify the logging or error reporting system.
      // You may alternatively send an email message or slack message here.
    }
    public void OnAuthorization(AuthorizationContext filterContext)
    {
      if (filterContext == null)
      {
        throw new ArgumentNullException("filterContext");
      }
      var httpContext = filterContext.HttpContext;
      var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
      try
      {
        AntiForgery.Validate(cookie != null ? cookie.Value : null, httpContext.Request.Headers["__RequestVerificationToken"]);
      }
      catch (Exception ex)
      {
        TellEveryoneWeBlockedAttacker(httpContext, ex);
      }
    }
  }
}

And decorate the action method with the ValidateHeaderAntiForgeryToken attribute.

using System.Web.Mvc;
using Online.Bank.App.Attributes;
namespace Online.Bank.App.Controllers
{
    public class AccountController : Controller
    {
        [HttpPost]
        [ValidateHeaderAntiForgeryToken] // This is where we put the Anti-Forgery attribute we just created
        public ActionResult Manage()
        {
            return Json(true); // This line is added for brevity. You would be doing good stuff here.
        }
    }
}

Watch how tidy the code became. Now we may decorate action methods and controllers throughout the entire ASP.NET MVC web application using this feature. The beauty of characteristics, I suppose.

Can we optimize the client-side code as well?

ASP.NET MVC generally has a _Layout.cshtml file. We can get the Anti-Forgery tokens there using JavaScript and @Html.AntiForgeryToken(). Though _Layout.cshtml would be the ideal spot, it can be done anywhere in your razor files.

<script>
function getAntiForgeryToken() {
     let token = '@Html.AntiForgeryToken()';
     token = $(token).val();
      return token;
}
</script>
Usage
<input type="button" onclick="manageAccount()" value="Manage Account" />
<script>
    function manageAccount() {
        $.ajax("<baseurl>/Account/Manage", {
            type: "post",
            contentType: "application/json",
            data: {  }, // JSON data
            dataType: "json",
            headers: {
                '__RequestVerificationToken': getAntiForgeryToken()
            },
            success: function (data) {
                alert(`Account Updated: ${data}`);
            }
        });
    }
</script>

That’s great. What if we want the Anti-Forgery token to be included in each request and we are using a lot of AJAX calls?

Using $.ajaxSetup(), you may configure jQuery requests so that the tokens are automatically included to the request header.

Usage is as follows.

$.ajaxSetup({
    headers: {
      '__RequestVerificationToken': getAntiForgeryToken()
    }
});

That’s it. We slightly complicated the attacker’s life by doing this. That’s good, isn’t it?

Conclusion

Without security scans, Cross-Site Request Forgery (CSRF) vulnerabilities are difficult to find. Applying the method described here (or any method, for that matter) would spare many people’s headaches, anguish, and suffering. Reactive programming is excellent for performance-related applications. But in terms of application security, being proactive is always preferable to being reactive. XSS, SQL Injections, and more attack methods are available in addition to CSRF.

Get your ASP.NET hosting as low as $1.00/month with ASPHostPortal. Our fully featured hosting already includes

  • Easy setup
  • 24/7/365 technical support
  • Top level speed and security
  • Super cache server performance to increase your website speed
  • Top 9 data centers across the world that you can choose.

 

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *