Breaking CSRF: ASP.NET MVC

Guest Editor: Today’s post is from Taras Kholopkin. Taras is a Solutions Architect at SoftServe. In this post, Taras will discuss using anti-forgery tokens in the ASP.NET MVC framework.

CSRF (Cross-Site Request Forgery) has been #8 in the OWASP Top 10 for many years. For a little background, CSRF is comprehensively explained in this article showing the attack, how it works, and how to defend your applications using synchronization tokens. Due to its prevalence, popular frameworks are building in CSRF protection for development teams to use in their web applications.

Protecting the ASP.NET MVC Web Application

This post will demonstrate how easily the ASP.NET MVC framework components can be used to protect your web application from CSRF attacks without any additional coding logic.

Generally, the protection from CSRF is built on top of two components:

  • @Html.AntiForgeryToken(): Special HTML helper that generates an anti-forgery token on the web page of the application (more details can be found here).
  • ValidateAntiForgeryToken: Action attribute that validates the anti-forgery token (more information can be found here).

They say practice makes perfect, so let’s walk through to a real-life demo. First, create a simple “Hello World” ASP.NET MVC web application in your Visual Studio. If you need help with this, all the necessary steps are described here.

The Client Side
After creating the web application in Visual Studio, go to the Solution Explorer panel and navigate to the main login page located here: Views/Account/Login.cshtml. We’ll use the built in CSRF prevention libraries to protect the login page of the web application.

The markup of the file is quite simple. A standard form tag with input fields for the username and password. The important piece for preventing CSRF can be seen on line 4 in the following snippet. Notice the inclusion of the AntiForgeryToken() HTML helper inside the form tag:

[sourcecode]
@using (Html.BeginForm(“Login”, “Account”, new { ReturnUrl = ViewBag.ReturnUrl },
FormMethod.Post, new { @class = “form-horizontal”, role = “form” }))
{
@Html.AntiForgeryToken()
<h4>Use a local account to log in.</h4>
<hr />
@Html.ValidationSummary(true, “”, new { @class = “text-danger” })
<div class=”form-group”>
@Html.LabelFor(m => m.Email, new { @class = “col-md-2 control-label” })
<div class=”col-md-10″>
@Html.TextBoxFor(m => m.Email, new { @class = “form-control” })
@Html.ValidationMessageFor(m => m.Email, “”, new { @class = “text-danger” })
</div>
</div>
<div class=”form-group”>
@Html.LabelFor(m => m.Password, new { @class = “col-md-2 control-label” })
<div class=”col-md-10″>
@Html.PasswordFor(m => m.Password, new { @class = “form-control” })
@Html.ValidationMessageFor(m => m.Password, “”, new { @class = “text-danger” })
</div>
</div>
}
[/sourcecode]

Let’s run the application and see what the anti-forgery token helper actually does. Browse to the login page via the menu bar. You should see a similar window on your screen:

Sample Login page

Right click in the page, and view the page source. You will see a hidden field added by the anti-forgery token helper called “__RequestVerificationToken”:

[sourcecode]
<input name=”__RequestVerificationToken” type=”hidden” value=”WhwU7C1dj4UHGEbYowHP9HZqWTDJDD8-VKAfl3f2vhMut3tvZY8OZIlMBOBL3NONMHlwUTZctE7BlrGKITG8tEEHVpTsV4Ul6Z6ijTVK_8M1″/>
[/sourcecode]

The Server Side
Next, lets shift our focus to the server side. Pressing the Log In button will POST the form fields, including the request verification token to the server. To view the data submitted, turn on the browser debugging tool in Internet Explorer (or any other network debugging tool) by pressing F12. Enter a username and password, then log in. The data model of the form request contains the anti-forgery token generated earlier on the server side:

Request verification token post parameter

The request reaches the server and will be processed by the account controller’s Login action. You can view the implementation to by going to Controllers/AccountController.cs file. As you can see, the ValidateAntiForgeryToken attribute on line 4 is in place above the action. This tells the framework to validate the request verification token that was sent to the browser in the view.

[sourcecode]
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task Login(LoginViewModel model, string returnUrl)
{

}
[/sourcecode]

For demo purposes, let’s remove the HTML helper @Html.AntiForgeryToken() on Login.cshtml view to simulate a malicious request from an attacker’s web site. Try to log in again. The server responds with an error because the ValidateAntiForgeryToken filter runs and cannot find the anti-forgery token in the request. This causes the application to stop processing the request before any important functionality is executed by the server. The result will look as follows:

AntiForgery validation error

Now that we’ve shown you how easy it is to set up CSRF protection in your ASP.NET MVC applications, make sure you protect all endpoints that make data modifications in your system. This simple, effective solution protects your MVC applications from one of the most common web application vulnerabilities.

To learn more about securing your .NET applications, sign up for DEV544: Secure Coding in .NET!

About the Author
Taras Kholopkin is a Solutions Architect at SoftServe, and a frequent contributor to the SoftServe United blog. Taras has worked more than nine years in the industry, including extensive experience within the US IT Market. He is responsible for software architectural design and development of Enterprise and SaaS Solutions (including Healthcare Solutions).

Breaking CSRF: Spring Security and Thymeleaf

As someone who spends half of their year teaching web application security, I tend to give a lot of presentations that include live demonstrations, mitigation techniques, and exploits. When preparing for a quality assurance presentation earlier this year, I decided to show the group a demonstration of Cross-Site Request Forgery (CSRF) and how to fix the vulnerability.

A CSRF Refresher
If you’re not familiar with Cross-Site Request Forgery (CSRF), check out the article Steve Kosten wrote earlier this year about the attack, how it works, and how to defend your applications using synchronization tokens:

The Demo
My favorite way to demonstrate the power of CSRF is by exploiting a vulnerable change password screen to take over a user’s account. The attack goes like this:

  • Our unsuspecting user signs into a web site, opens a new tab, and visits my evil web page
  • My evil web page submits a forged request
  • The server changes the user’s password to a value that I know, and
  • I own their account!

I know, I know. Change password screens are supposed to ask for the user’s previous password, and the attacker wouldn’t know what that is! Well, you’re absolutely right. But, you’d be shocked at the number of web applications that I test each year that are missing this simple security mechanism.

Now, let’s create our vulnerable page.

The Change Password Page
My demo applications in the Java world use Spring MVC, Spring Security, Thymeleaf, and Spring JPA. If you’re not familiar with Thymeleaf, see the References section, and check out the documentation. This framework provides an easy-to-use HTML template engine that allows us to quickly create views for a web application. I created the following page called changepassword.html containing inputs for new password and confirm password, and a submit button:

[sourcecode]
<form method=”post” th:action=”@{/content/changepassword}”
th:object=”${changePasswordForm}”>
<table cellpadding=”0″>
<tr>
<td align=”right”>New Password:</td>
<td>
<input id=”newPassword” type=”password” th:field=”*{newPassword}” />
</td>
</tr>
<tr>
<td align=”right”>Confirm Password:</td>
<td>
<input id=”confirmPassword” type=”password” th:field=”*{confirmPassword}” />
</td>
</tr>
<tr>
<td>
<input type=”submit” value=”Change Password” id=”btnChangePassword” />
</td>
</tr>
</table>
</form>
[/sourcecode]

The Change Password HTML
After creating the server-side controller to change the user’s password, I fired up the application, signed into my demo site, and browsed to the change password screen. In the HTML source I found something very interesting. A mysterious hidden field, “_csrf”, had been added to my form:

[sourcecode light=”true”]
<input type=”hidden” name=”_csrf” value=”aa1688de-3984-448a-a56e-43bfa027790c” />
[/sourcecode]

When submitting the request to change my password, the raw HTTP request sent the _csrf request parameter to the server and I received a successful 200 response code.

[sourcecode light=”true”]
POST /csrf/content/changepassword HTTP/1.1
Host: localhost:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Cookie: JSESSIONID=2A801D28758DFF67FCDEC7BE180857B8
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded

newPassword=Stapler&confirmPassword=Stapler&_csrf=bd729b66-8d90-46c8-94ff-136cd1188caa
[/sourcecode]

“Interesting,” I thought to myself. Is this token really being validated on the server-side? I immediately fired up Burp Suite, captured a change password request, and sent it to the repeater plug-in.

For the first test, I removed the “_csrf” parameter, submitted the request, and watched it fail.

[sourcecode light=”true”]
POST /csrf/content/changepassword HTTP/1.1
Host: localhost:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Cookie: JSESSIONID=2A801D28758DFF67FCDEC7BE180857B8
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded

newPassword=Stapler&confirmPassword=Stapler
[/sourcecode]

For the second test, I tried again with an invalid “_csrf” token.

[sourcecode light=”true”]
POST /csrf/content/changepassword HTTP/1.1
Host: localhost:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Cookie: JSESSIONID=2A801D28758DFF67FCDEC7BE180857B8
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded

newPassword=Stapler&confirmPassword=Stapler&_csrf=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
[/sourcecode]

Both test cases returned the same 403 Forbidden HTTP response code:

[sourcecode light=”true”]
HTTP/1.1 403 Forbidden
Server: Apache-Coyote/1.1
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: text/html;charset=UTF-8
Content-Language: en-US
Content-Length: 2464
Date: Thu, 27 Aug 2015 11:48:08 GMT
[/sourcecode]

Spring Security
As an attacker trying to show how powerful CSRF attacks can be, this is troubling. As a defender, this tells me that my application’s POST requests are protected from CSRF attacks by default! According to the Spring documentation, CSRF protection has been baked into Spring Security since v3.2.0 was released in August 2013. For applications using the Java configuration, this is enabled by default and explains why my change password demo page was already being protected.

However, applications using XML-based configuration on versions before Spring v4.0 still need to enable the feature in the http configuration:

[sourcecode light=”true”]
<http>

<csrf />
</http>
[/sourcecode]

Applications on Spring v4.0+ have this protection enabled by default, regardless of how they are configured, making it easy for code reviewers and static analyzers to find instances where it is disabled. Of course this is exactly what I had to do for my presentation demo to work, but it’s a security anti-pattern so we don’t discuss how to do this here. See the Reference section for more details on Spring’s CSRF protections.
 
Caveats
Before we run around the office saying our applications are safe because we’re using Thymeleaf and Spring Security, I’m going to identify a few weaknesses in this defense.

  • Thymeleaf and Spring Security only place CSRF tokens into “form” elements. This means that only form actions submitting POST parameters are protected. If your application makes data modifications via GET requests or AJAX calls, you still have work to do.
  • Cross-Site Scripting (XSS) attacks can easily extract CSRF tokens to an attacker’s remote web server. If you have XSS vulnerabilities in your application, this protection does you no good.
  • My demo application is using Thymeleaf 2.1.3 and Spring 4.1.3. Older versions of these frameworks may not provide the same protection. As always, make sure you perform code reviews and security testing on your applications to ensure they are secure before deploying them to production.

References:

To learn more about securing your Java applications, sign up for DEV541: Secure Coding in Java/JEE!
 
About the Author
Eric Johnson (Twitter: @emjohn20) is a Senior Security Consultant at Cypress Data Defense, Application Security Curriculum Product Manager at SANS, and a certified SANS instructor. He is the lead author and instructor for DEV544 Secure Coding in .NET, as well as an instructor for DEV541 Secure Coding in Java/JEE. Eric serves on the advisory board for the SANS Securing the Human Developer awareness training program and is a contributing author for the developer security awareness modules. Eric’s previous experience includes web and mobile application penetration testing, secure code review, risk assessment, static source code analysis, security research, and developing security tools. He completed a bachelor of science in computer engineering and a master of science in information assurance at Iowa State University, and currently holds the CISSP, GWAPT, GSSP-.NET, and GSSP-Java certifications.

Demystifying Cross-Site Request Forgery

Continuously ranked in the OWASP Top Ten, a large majority of the development community still doesn’t understand Cross-Site Request Forgery (CSRF). After years of penetration tests and code reviews, my experiences show that a high percentage of applications, especially new applications, do not have proper CSRF protections in place. This post provides a refresher on CSRF and provides a common defense for this issue.

The Exploit

CSRF occurs when an application trusts that all requests originating from the user’s browser are user-directed actions. Imagine that you are logged into your bank’s online portal. The application requires users to authenticate and passes a session cookie back to the browser. Subsequent requests made to the banking site must contain the session cookie, allowing the site to identify the user and perform the requested action.

What if an attacker could send a fake request using the victim’s browser?

Suppose an attacker wants to transfer money from a victim’s account to their own account. In an unprotected site, the attacker simply makes a valid transfer between accounts in the web site and captures the request.

The following snippet shows a hypothetical request to transfer funds:

GET /transfer.jsp?from=attacker&to=attacker&amount=1 HTTP/1.1
Host: bank.com
Cookie: JSESSIONID=aa968b1d26a891d451e02c631553d2f8f56446bd;

The attacker could then generate a malicious request, such as the src property shown in the image tag below:

<img src=”http://bank.com/transfer.jsp?from=victim&to=attacker&amount=1000000″ />

To execute this attack, our attacker uses a little social engineering and sends our victim two phishing emails. The first tells the victim about some malicious activity on their banking account. The second adversities a free vacation, and contains a link to an evil web page containing the image shown above.

The victim views the emails and quickly signs in to the banking web site. After verifying everything looks ok, the victim clicks the link for a free vacation. As long as the victim is still authenticated to the banking site, the browser will conveniently include the active banking session id in the request:

GET /transfer.jsp?from=victim&to=attacker&amount=1000000 HTTP/1.1
Host: bank.com
Cookie: JSESSIONID=2a63ad7a0ce1ab65ecc293f622587c224e8d4e21;

Notice that this looks identical to the original request, except the session id from the victim’s browser has been automatically included in the request. From the server’s point of view, this looks like a valid, user-directed request and the transfer succeeds!

The Defense

CSRF defenses commonly use synchronization tokens. This solution adds a random token to each request that modifies data or performs a transaction. As the request is processed, the application verifies that the value received in the request matches the value generated on the server. The following snippet shows what the transfer request protected with a CSRF token could look like. Notice that we have switched the GET request to a POST request. It is a best practice to make all data modifications and transactions using a POST request, rather than a GET request:

POST /transfer.jsp HTTP/1.1
Host: bank.com
Cookie: JSESSIONID=2a63ad7a0ce1ab65ecc293f622587c224e8d4e21;

from=victim&to=attacker&amount=1000000&token=444a6130-9d2a-11e4-bd06-0800200c9a66

The new token parameter has been added to the POST parameters. As long as the value is sufficiently random, the attacker will be unable to guess the victim’s current value as the attack is taking place. If the application properly verifies the token before processing the transfer, the attack will fail!

OWASP’s CSRFGuard is a popular Java EE filter that can be configured to protect an entire web site from CSRF attacks by automatically adding and verifying CSRF tokens.

To learn more about protecting your Java and web-based applications from CSRF using OWASP CSRFGuard, sign up for DEV541: Secure Coding in Java!

References:
https://www.owasp.org/index.php/Category:OWASP_CSRFGuard_Project

About the Author
Steve Kosten is a security consultant at Cypress Data Defense and the Denver Chapter President of the Open Web Application Security Project (OWASP) that focuses on information security education related to software applications. He was the co-organizer of AppSec USA 2014. He is an application security specialist who reviews software applications for top 100 firms across multiple industries including the financial, defense, identity management and more. He has a Masters degree in Information Security, and is CISSP and CISM certified.