top of page

CSRF Protection in ASP.NET Core: A Comprehensive Guide

Cross-Site Request Forgery (CSRF) is a security vulnerability that can compromise the integrity of web applications. In a CSRF attack, an attacker tricks a user’s browser into making unauthorized requests to a different site where the user is authenticated. These requests can lead to unintended actions, data manipulation, or security breaches.


ASP.NET Core provides robust mechanisms to prevent CSRF attacks by using anti-forgery tokens. This comprehensive guide will help you explore how to implement CSRF protection in your ASP.NET Core applications. We’ll cover the essential concepts, best practices, and practical steps to secure your web application.


What is a CSRF Attack?

CSRF (Cross-Site Request Forgery) is an attack where an attacker tricks a user into acting on a web application without their knowledge or consent. The attack occurs when the user is authenticated and interacts with seemingly legitimate requests (e.g., clicking a link or submitting a form).


How Does It Work?

User Authentication:

  • The victim (user) is logged in to a web application (e.g., online banking).

  • Their session includes authentication cookies or tokens.


Malicious Request:

The attacker crafts a deceptive request (e.g., fund transfer, password change) and embeds it in a seemingly harmless link or form.


Example:

  • The attacker sends an email with a link saying, “Click here to win a prize!” The victim believes it’s a harmless offer.

  • However, the link initiates a fund transfer from the victim’s account to the attacker’s.


User Interaction:

  • The unsuspecting victim clicks the link or submits the form.

  • Their browser automatically includes the relevant cookies or tokens (since they are authenticated).

  • The victim doesn't know they are acting on behalf of the attacker.


Impact:

The attacker successfully performs unauthorized actions using the victim’s credentials:

  • Funds are transferred from the victim’s account to the attacker’s account.

  • The victim’s email address, password, or other account settings may be changed.

  • Unauthorized purchases or modifications occur.


The severity of the impact depends on the victim’s role (normal user vs. administrative account).


Impact of CSRF Attacks:

If successful, the CSRF attack forces to perform actions without their knowledge or consent. Here are some examples:

  • Transferring Funds: The attacker can transfer funds from the victim’s account to the attacker’s.

  • Changing Email or Password: The attacker can modify the victim’s email address or password.

  • Unauthorized Purchases: The attacker can make unauthorized purchases on behalf of the victim.


The impact varies based on the victim’s role:

  • For normal users, it can lead to financial loss or unauthorized changes.

  • For administrative accounts, it can compromise the entire web application.


Understanding Anti-Forgery Tokens

Anti-forgery tokens (or request verification tokens) are essential for preventing Cross-Site Request Forgery (CSRF) attacks. These attacks occur when a malicious site tricks a user’s browser into making unauthorized requests to another site where the user is authenticated. To defend against CSRF, ASP.NET Core provides built-in mechanisms.


The Role of Anti-forgery Tokens are:

  • Introduce an additional validation layer to ensure requests originate from the intended user’s session.

  • Prevent attackers from forging requests by requiring a specific token to be included in each request.


How Anti-Forgery Protection Works?

When a user visits a web page (e.g., a form for fund transfer), the server generates a unique anti-forgery token. This token is specific to the user’s session and is cryptographically random. The server sends this token to the client as an HTTP cookie. The user interacts with the form (e.g., fills out details, and clicks “Submit”). The client (browser) includes the anti-forgery token in the form data. The token is also embedded as a hidden field within the form. The request is sent to the server when the user submits the form. The server receives the form data, including the anti-forgery token.


The server validates the token:

  • The request is legitimate if the token matches the one stored in the cookie (i.e., the client’s browser sent the correct token).

  • If the tokens don’t match or are missing, the server rejects the request as a potential CSRF attack.


Why Are Anti-Forgery Tokens Essential?

Anti-forgery tokens play a critical role in web application security:

  • Preventing Unauthorized Actions:

  • By validating tokens, web applications ensure that only legitimate user actions are processed.

  • Malicious requests (crafted by attackers) are rejected.

  • This prevents unauthorized changes, financial loss, and other security breaches.

  • Role-Based Impact:

  • For normal users, anti-forgery tokens protect against financial loss and unauthorized modifications.

  • For administrative accounts, they prevent compromising the entire application.


Setting Up Anti-Forgery Protection in ASP.NET Core

Settings up the Anti-Forgery Protection in ASP.NET core includes:

  1. Enabling Anti-Forgery Protection

  2. Generating Anti-Forgery Tokens


STEP 1: Enable Anti-Forgert Protection

To enable anti-forgery protection in an ASP.NET Core application, follow these steps:


STEP A: Add the Anti-Forgery Middleware:

In your Startup.cs file, configure the anti-forgery middleware by adding the following line inside the ConfigureServices method:

services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");

STEP B: Include the Anti-Forgery Token in Forms:

In your Razor views, include the anti-forgery token inside your forms using the @Html.AntiForgeryToken() helper:

<form action="/YourController/YourAction" method="post"> @Html.AntiForgeryToken() 
<!-- Other form fields --> 
<button type="submit">Submit</button> 
</form>

STEP C: Validate the Token in Your Controller Action:

In your controller action that handles the form submission, decorate it with the [ValidateAntiForgeryToken] attribute:

[HttpPost] [ValidateAntiForgeryToken] public IActionResult YourAction(YourViewModel model) { // Process the form data // ... return View(); }

STEP D: Enforce Global Anti-Forgery Validation (Optional):

To enforce anti-forgery validation globally for all controllers, add the following line inside the ConfigureServices method:

services.AddControllers(options => { options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); });

STEP E: Customize Anti-Forgery Options (Optional):

  • You can customize anti-forgery options by modifying the options object in the AddAntiforgery configuration.

  • For example, you can set the cookie name, path, and other properties.


STEP 2: Generating Anti-Forgery Tokens

The tokens are generated randomly to prevent guessing, by adversaries. When the client submits the form, it sends back tokens to the server. The server validates the tokens to ensure the request is legitimate.


STEP A: Add the Token to Your Form:

In your Razor view, include the @Html.AntiForgeryToken() helper inside your form. This generates a hidden input field containing the anti-forgery token.


Example:

<form action="/MyController/MyAction" method="post"> @Html.AntiForgeryToken() <!-- Other form fields --> <button type="submit">Submit</button> </form>

STEP B: Decorate Your Action Method:

In your controller, decorate the action method that handles the form submission with the [ValidateAntiForgeryToken] attribute.


Example:

[HttpPost] [ValidateAntiForgeryToken] public IActionResult MyAction(MyViewModel model) { // Process the form data // ... return View(); }

Additional Considerations

Clear Previous Cookies:

  • It ensures that users clear their previous cookies when you modify anti-forgery settings (e.g., token generation, validation rules).

  • Why? They might conflict with the new settings, leading to unexpected behavior if users have stored old anti-forgery cookies.

  • Clearing cookies ensures a fresh start with the updated anti-forgery mechanism.


Secure Flag:

  • To enhance security, consider setting the Secure flag for the anti-forgery cookie.

  • What does it do? When the Secure flag is set, the cookie is sent only over HTTPS connections.

  • Why is this important? It prevents the cookie from being transmitted over insecure channels (e.g., plain HTTP), reducing the risk of interception by attackers.


Methods to Implement Anti-forgery Protection

Here are some methods to implement anti-forgery protection in web applications:

  1. Double-Submit Cookies

  2. Synchronizer Token Pattern


These techniques help prevent Cross-Site Request Forgery (CSRF) attacks, where malicious sites manipulate interactions between a user’s browser and a trusted web app.

Aspect

Double-Submit Cookies

Synchronizer Token Pattern

Description

The server generates a random token and sets it as both an HTTP cookie and a hidden form field value. The user submits a form with both tokens. The Server compares the cookie token with the form token.

The server generates a unique token and associates it with the user’s session. The token is included in the form as a hidden field. - Server validates form token against session token.

Security Strength

- Moderate: Vulnerable to cross-site scripting (XSS) attacks (since the token is stored in a cookie). It requires JavaScript to read and set the cookie token.

- High: Resistant to XSS attacks. Token not exposed in cookies.

Server-Side Storage

- None required (stateless).

- Requires server-side storage for tokens (session-based).

Complexity

- Simple to implement.

- Slightly more complex due to server-side handling.

Token Storage

- Cookie Token: Stored as an HTTP cookie in the user’s browser. - Form Token: Included as a hidden field in the form.

- Session Token: Stored on the server side (associated with the user’s session). It is included in the form as a hidden field.

Suitability for SPAs

- Not ideal for single-page applications (SPAs) due to cookie reliance.

- Ideal for SPAs with server-side session management.

Token Renewal Handling

-N/A (Stateless)

- Need to handle token expiration and renewal.

Use Cases

- Traditional web applications.

- SPAs and applications with server-side session management.


When to use each approach?

Double-Submit Cookies:

  • When to Use:

  • Choose this method when simplicity is crucial.

  • Ideal for traditional web applications.

  • Stateless applications benefit from its straightforward implementation.

  • Considerations:

  • Vulnerable to cross-site scripting (XSS) attacks (since the token is stored in a cookie).

  • Requires JavaScript for cookie handling.

  • Not suitable for single-page applications (SPAs).


Synchronizer Token Pattern:

  • When to Use:

  • Opt for this approach when higher security is essential.

  • Perfect for SPAs and applications with server-side session management.

  • Resistant to XSS attacks (token not exposed in cookies).

  • Considerations:

  • Requires server-side storage for tokens.

  • Slightly more complex to implement.


Prevent CSRF Attacks in ASP.NET Core

Here are some of the solutions to prevent CSRF attacks in ASP.NET Web Applications:


Solution 1: Use Anti-Forgery Tokens with Any Authentication Protocol:

When a user logs in, the browser often silently sends credentials (such as cookies or tokens) to the server for authentication. CSRF attacks exploit this trust by tricking users into performing unauthorized actions.


To prevent CSRF attacks, include anti-forgery tokens in your application. These tokens act as a safeguard against unauthorized requests.


You can integrate anti-forgery tokens regardless of the authentication protocol (e.g., forms authentication, OAuth, or OpenID Connect).


Configure Services in Startup.cs:

public void ConfigureServices(IServiceCollection services) 
{ 
	// Other services... 
	services.AddControllersWithViews(); 
	services.AddAntiforgery(options => 
	{ 
		options.HeaderName = "X-CSRF-TOKEN"; 
		// Customize the header name 
	}); 
}

Controller Action Example (e.g., AccountController):

[HttpPost] 
[ValidateAntiForgeryToken] // Apply the attribute to protect against CSRF 

public IActionResult SomeAction(SomeModel model) 
{ 
	// Your action logic here 
	// ... 
	return View(); 
}

We configure anti-forgery services in Startup.cs using services.AddAntiforgery().


The ValidateAntiForgeryToken attribute ensures that the token in the request matches the one generated by the server.


Solution 2: Two Different Values Sent to the Server with Each POST Request:

When a user submits a form (via a POST request), your application should send two distinct values to the server:

  • Browser Cookie: An anti-forgery token is stored as a cookie in the user’s browser. This token is automatically included in subsequent requests.

  • Form Data: Another anti-forgery token is embedded in the form data itself (usually as a hidden field). This value is sent explicitly with the form submission.


The server compares these two tokens during request processing to ensure they match. If they don’t, the request is considered suspicious and rejected.


We achieve this by including the anti-forgery token both as a browser cookie and in the form data:

<!-- In your Razor view (e.g., MyForm.cshtml): -->
<form method="post">
    @Html.AntiForgeryToken() <!-- Generates the hidden form field -->
    <!-- Other form fields... -->
    <button type="submit">Submit</button>
</form>

@Html.AntiForgeryToken() generates a hidden input field containing the anti-forgery token. The same token is also stored as a browser cookie.


Solution 3: Anti-CSRF Tokens (Token-Based Approach):

Example in Python (using Flask):

from flask import Flask, request, render_template_string, session

app = Flask(__name__)
app.secret_key = 'your-secret-key'

@app.route('/transfer', methods=['POST'])
def transfer_funds():
    if request.method == 'POST':
        # Verify CSRF token
        if request.form.get('csrf_token') == session.get('csrf_token'):
            # Process the fund transfer
            # ...
            return "Funds transferred successfully!"
        else:
            return "CSRF token validation failed."
    return "Invalid request method."

@app.route('/')
def index():
    # Generate and store CSRF token in session
    session['csrf_token'] = 'random-token-here'
    return render_template_string("""
        <form action="/transfer" method="post">
            <input type="hidden" name="csrf_token" value="{{ session['csrf_token'] }}">
            <!-- Other form fields -->
            <button type="submit">Transfer Funds</button>
        </form>
    """)

if __name__ == '__main__':
    app.run()

Importance

  • Anti-CSRF tokens prevent attackers from forging requests on behalf of authenticated users.

  • By requiring a specific token for each request, the server ensures that actions originate from legitimate sessions.


Solution 4: Use SameSite Cookies

The SameSite attribute controls how cookies are sent in cross-origin requests. It helps prevent certain security vulnerabilities, such as Cross-Site Request Forgery (CSRF) attacks.


When you set a cookie with the SameSite attribute, you can specify one of three values:

  1. "Strict": The cookie is only sent in first-party (same-site) requests. It is not sent in cross-origin requests.

  2. "Lax": The cookie is sent in first-party and some cross-origin requests (such as top-level navigation). It is not sent in unsafe cross-origin requests (e.g., POST requests).

  3. "None": The cookie is sent in all requests, including cross-origin requests. However, this requires the Secure attribute (i.e., the request must be over HTTPS).


Example Implementation in Node.js with Express:

const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();

// Use cookie-parser middleware
app.use(cookieParser());

// Route for handling fund transfer (POST request)
app.post('/transfer', (req, res) => {
    // Verify CSRF token (not shown here)
    // Process the fund transfer
    // ...
    res.send('Funds transferred successfully!');
});

// Route for serving the form (GET request)
app.get('/', (req, res) => {
    // Set SameSite cookie with "strict" attribute
    res.cookie('csrf_token', 'random-token-here', { sameSite: 'strict' });
    res.send(`
        <form action="/transfer" method="post">
            <input type="hidden" name="csrf_token" value="random-token-here">
            <!-- Other form fields -->
            <button type="submit">Transfer Funds</button>
        </form>
    `);
});

// Start the server
app.listen(3000, () => {
    console.log('Server running on port 3000');
});

Notes:

  • In the example above, we set the csrf_token cookie with the "strict" SameSite attribute. This ensures that the cookie is only sent in same-site requests.

  • Remember to adapt this example to your specific web framework and requirements. You might need to handle CSRF token verification and other aspects based on your application’s needs.


Conclusion

This guide explored how to protect ASP.NET Core applications from CSRF attacks using anti-forgery tokens. By following best practices and integrating anti-forgery features directly within your controllers, you can enhance the security of your web applications.

Comments


bottom of page