> ## Documentation Index
> Fetch the complete documentation index at: https://guide.codepure.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cross-Site Request Forgery (CSRF)

> Mitigation for Cross-Site Request Forgery in Django, Spring Boot, Rails, Express, ASP.NET Core, and Laravel.

## Overview

Cross-Site Request Forgery (CSRF) is an attack that tricks a victim's browser into submitting an unintended request to an application where they are already authenticated. This can lead to unauthorized actions like changing a password, making a purchase, or deleting an account, all without the user's knowledge. The attack works because the browser automatically sends authentication cookies with the request.

## Business Impact

CSRF can lead to unauthorized financial transactions, data modification, or full account takeover (if used to change a user's email or password). It erodes user trust and can cause significant financial and reputational damage.

<Card title="Reference Details" icon="book-open" iconType="solid">
  **CWE ID:** [CWE-352](https://cwe.mitre.org/data/definitions/352.html)
  **OWASP Top 10 (2021):** A01:2021 - Broken Access Control
  **Severity:** High
</Card>

## Framework-Specific Analysis and Remediation

Modern web frameworks (Rails, Django, Laravel, Spring Security, ASP.NET) have built-in CSRF protection enabled by default for session-based authentication. The vulnerability is *not* that the framework is weak, but that a developer *disables* this protection, often for convenience (e.g., for an API) or by mistake. The fix is to re-enable and correctly use the framework's anti-CSRF token mechanism.

<Tabs>
  <Tab title="Python">
    #### Framework Context

    Django's `CsrfViewMiddleware` is enabled by default. It requires a `{% csrf_token %}` in all `POST` forms. The vulnerability is using the `@csrf_exempt` decorator on a view.

    #### Vulnerable Scenario 1: Exempting a Function-Based View

    A view that changes a user's email is explicitly exempted from CSRF checks for convenience.

    ```python theme={null}
    # users/views.py
    from django.views.decorators.csrf import csrf_exempt
    from django.contrib.auth.decorators import login_required

    @login_required
    @csrf_exempt # DANGEROUS
    def update_email(request):
        if request.method == 'POST':
            # This action can be triggered from a malicious site
            request.user.email = request.POST.get('email')
            request.user.save()
        return render(request, 'profile.html')
    ```

    #### Vulnerable Scenario 2: Exempting a Class-Based View

    A `FormView` for deleting an account is exempted using `@method_decorator`.

    ```python theme={null}
    # users/views.py
    from django.utils.decorators import method_decorator
    from django.views.decorators.csrf import csrf_exempt
    from django.views.generic import FormView

    @method_decorator(csrf_exempt, name='dispatch') # DANGEROUS
    class DeleteAccountView(FormView):
        # ... form_class and success_url ...
        
        def form_valid(self, form):
            # This deletion can be triggered by a malicious POST
            self.request.user.delete()
            return super().form_valid(form)
    ```

    #### Mitigation and Best Practices

    Remove the `@csrf_exempt` and `@method_decorator(csrf_exempt)` decorators. Ensure your `POST` form in the template includes the `{% csrf_token %}` tag. For AJAX, pass the token in a custom `X-CSRFToken` header.

    #### Secure Code Example

    ```python theme={null}
    # users/views.py (Secure Version)
    @login_required
    # @csrf_exempt decorator is REMOVED
    def update_email(request):
        if request.method == 'POST':
            request.user.email = request.POST.get('email')
            request.user.save()
        return render(request, 'profile.html')

    # templates/profile.html (Secure Version)
    /*
    <form method="post">
        {% csrf_token %} <label>Email:</label>
        <input type="email" name="email">
        <button type="submit">Update</button>
    </form>
    */
    ```

    #### Testing Strategy

    Write an integration test that performs a `POST` request to the endpoint *without* the CSRF token. The test should assert that the response is a `403 Forbidden`.

    ```python theme={null}
    # users/tests.py
    def test_update_email_fails_without_csrf_token(self):
        # self.client is logged in
        response = self.client.post(reverse('update-email'), {
            'email': 'new@example.com'
        })
        
        # A secure endpoint will return 403 Forbidden
        self.assertEqual(response.status_code, 403)
    ```
  </Tab>

  <Tab title="Java">
    #### Framework Context

    Spring Security enables CSRF protection by default. It requires a unique token for all state-changing methods (POST, PUT, DELETE). The vulnerability is disabling it.

    #### Vulnerable Scenario 1: Disabling CSRF Globally

    In the `WebSecurityConfigurerAdapter`, a developer disables CSRF protection entirely.

    ```java theme={null}
    // config/SecurityConfig.java
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .and()
            .csrf().disable(); // DANGEROUS: CSRF protection is off globally
    }
    ```

    #### Vulnerable Scenario 2: Disabling CSRF for Specific Routes

    A developer disables CSRF for all routes under `/api`, thinking it's only for a stateless API, but a stateful (session-based) endpoint is accidentally included.

    ```java theme={null}
    // config/SecurityConfig.java
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // DANGEROUS: If /api/profile/update uses session cookies,
            // it is now vulnerable to CSRF.
            .csrf().ignoringAntMatchers("/api/**")
            .and()
            .authorizeRequests()
                .antMatchers("/api/profile/**").authenticated() // Session-based
                .antMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated();
    }
    ```

    #### Mitigation and Best Practices

    Remove the `.csrf().disable()` call. If you must disable CSRF for a stateless API, ensure those routes (`.antMatchers("/api/stateless/**")`) are *truly* stateless (e.g., use JWT bearer tokens) and that your session-based routes remain protected.

    #### Secure Code Example

    ```java theme={null}
    // config/SecurityConfig.java (Secure Version)
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin();
        // SECURE: .csrf().disable() is removed. Protection is ON.
    }

    // templates/profile.html (Secure Version with Thymeleaf)
    /*
    <form th:action="@{/update-profile}" method="post">
        <label>Email:</label>
        <input type="email" name="email">
        <button type="submit">Update</button>
    </form>
    */
    ```

    #### Testing Strategy

    Write a MockMVC test using `@WithMockUser`. Perform a `post()` request *without* the `csrf()` request processor. Assert the response is a `403 Forbidden`.

    ```java theme={null}
    @Test
    @WithMockUser
    void updateProfile_withoutCsrfToken_shouldBeForbidden() throws Exception {
        mockMvc.perform(post("/update-profile")
                .param("email", "new@example.com"))
            .andExpect(status().isForbidden());
    }

    @Test
    @WithMockUser
    void updateProfile_withCsrfToken_shouldSucceed() throws Exception {
        mockMvc.perform(post("/update-profile")
                .param("email", "new@example.com")
                .with(csrf())) // SECURE: Adds a valid CSRF token
            .andExpect(status().isOk());
    }
    ```
  </Tab>

  <Tab title=".NET(C#)">
    #### Framework Context

    ASP.NET Core automatically generates and validates anti-forgery tokens for Razor Pages and MVC forms created with tag helpers. The vulnerability is a developer forgetting to add the `[ValidateAntiForgeryToken]` attribute or explicitly ignoring it.

    #### Vulnerable Scenario 1: Missing Validation Attribute

    An endpoint that modifies data is missing the validation attribute.

    ```csharp theme={null}
    // Controllers/ProfileController.cs
    [Authorize]
    public class ProfileController : Controller
    {
        [HttpPost]
        // DANGEROUS: This action does not validate the anti-forgery token.
        public async Task<IActionResult> Update(string email)
        {
            // ... update logic
            return RedirectToAction("Index");
        }
    }
    ```

    #### Vulnerable Scenario 2: Globally Ignoring Validation

    A developer globally disables anti-forgery token validation in `Startup.cs`.

    ```csharp theme={null}
    // Startup.cs
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews(options =>
        {
            // DANGEROUS: This globally disables CSRF validation for all POST actions.
            options.Filters.Add<IgnoreAntiforgeryTokenAttribute>();
        });
    }
    ```

    #### Mitigation and Best Practices

    Add the `[ValidateAntiForgeryToken]` attribute to all `[HttpPost]`, `[HttpPut]`, and `[HttpDelete]` actions. For a safer "deny by default" approach, add `[AutoValidateAntiforgeryToken]` as a global filter in `Startup.cs`, which automatically protects all state-changing methods.

    #### Secure Code Example

    ```csharp theme={null}
    // Startup.cs (Secure - Global)
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews(options =>
        {
            // SECURE: Automatically validates tokens on all non-GET methods.
            options.Filters.Add<AutoValidateAntiforgeryTokenAttribute>();
        });
    }

    // Controllers/ProfileController.cs (Secure - Method-specific)
    [Authorize]
    public class ProfileController : Controller
    {
        [HttpPost]
        [ValidateAntiForgeryToken] // SECURE: This attribute enables validation.
        public async Task<IActionResult> Update(string email)
        {
            // ... update logic
            return RedirectToAction("Index");
        }
    }

    // Views/Profile/Index.cshtml (Secure Version)
    /*
    <form asp-controller="Profile" asp-action="Update" method="post">
        <input type-="email" name="email" />
        <button type="submit">Update</button>
    </form>
    */
    ```

    #### Testing Strategy

    Write an integration test that authenticates a client. The test then creates an `HttpRequestMessage` for a `POST` but *does not* include the `__RequestVerificationToken` cookie or form data. Assert the response is a `400 Bad Request`.

    ```csharp theme={null}
    [Fact]
    public async Task Update_Without_AntiForgeryToken_ShouldFail()
    {
        // _client is authenticated
        var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Profile/Update");
        var formData = new Dictionary<string, string> { { "email", "new@example.com" } };
        postRequest.Content = new FormUrlEncodedContent(formData);

        // We are NOT adding the token, so this should fail
        var response = await _client.SendAsync(postRequest);
        
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
    }
    ```
  </Tab>

  <Tab title="PHP">
    #### Framework Context

    Laravel's `VerifyCsrfToken` middleware is enabled by default for the `web` route group. The vulnerability is adding a state-changing route to the `$except` array or incorrectly using cookie-based auth on an "API" route.

    #### Vulnerable Scenario 1: Exempting a Route

    A developer exempts the profile update route from CSRF protection in the middleware.

    ```php theme={null}
    // app/Http/Middleware/VerifyCsrfToken.php
    class VerifyCsrfToken extends Middleware
    {
        protected $except = [
            // DANGEROUS: This route can now be attacked via CSRF
            '/profile/update',
        ];
    }
    ```

    #### Vulnerable Scenario 2: Using Session Auth on API Route

    The `routes/api.php` file does *not* have CSRF protection. A developer defines a route here but authenticates using the web session cookie instead of a stateless token.

    ```php theme={null}
    // routes/api.php

    // DANGEROUS: If a user is logged into the main app (with a session),
    // and this route uses that session cookie for auth, it is vulnerable
    // to CSRF because the `api` middleware group is stateless by default.
    Route::post('/user/delete', [UserController::class, 'delete'])
         ->middleware('auth:web'); // Using web auth in API
    ```

    #### Mitigation and Best Practices

    Remove the route from the `$except` array. Ensure your Blade template form includes the `@csrf` directive. Routes in `api.php` must be stateless (e.g., use Sanctum API tokens or JWTs), not web session cookies.

    #### Secure Code Example

    ```php theme={null}
    // app/Http/Middleware/VerifyCsrfToken.php (Secure Version)
    class VerifyCsrfToken extends Middleware
    {
        // SECURE: The route is no longer in the exception list.
        protected $except = [
            //
        ];
    }

    // resources/views/profile.blade.php (Secure Version)
    /*
    <form method="POST" action="/profile/update">
        @csrf <input type="email" name="email">
        <button type="submit">Update</button>
    </form>
    */
    ```

    #### Testing Strategy

    Write a feature test. `actingAs` a user, then make a `post` request to the endpoint *without* the `_token` data. Assert the response is a `419` (Session Expired / Token Mismatch).

    ```php theme={null}
    // tests/Feature/ProfileUpdateTest.php
    public function test_profile_update_fails_without_csrf_token()
    {
        $user = User::factory()->create();
        
        $response = $this->actingAs($user)
                         ->post('/profile/update', [
                             'email' => 'new@example.com'
                         ]);
        
        // 419 is Laravel's status code for CSRF failure
        $response->assertStatus(419);
    }
    ```
  </Tab>

  <Tab title="Node.js">
    #### Framework Context

    Express has **no built-in CSRF protection**. The vulnerability is simply not using a library to add it, or misconfiguring it. The standard library is `csurf`.

    #### Vulnerable Scenario 1: No CSRF Middleware

    A standard Express app with `cookie-session` but no CSRF middleware.

    ```javascript theme={null}
    // app.js
    const express = require('express');
    const app = express();

    app.use(require('cookie-session')({ keys: ['secret'] }));
    app.use(express.urlencoded({ extended: false }));

    app.post('/profile/update', (req, res) => {
        // DANGEROUS: No CSRF token is checked. Any site can
        // POST to this endpoint and the user's cookie will be sent.
        req.session.user.email = req.body.email;
        res.redirect('/profile');
    });
    ```

    #### Vulnerable Scenario 2: Middleware Applied Incorrectly

    The `csurf` middleware is added, but *after* the route it's supposed to protect.

    ```javascript theme={null}
    // app.js
    const csrf = require('csurf');

    app.use(express.urlencoded({ extended: false }));

    // DANGEROUS: The vulnerable route is defined before
    // the CSRF middleware is applied.
    app.post('/profile/update', (req, res) => {
        req.session.user.email = req.body.email;
        res.redirect('/profile');
    });

    // The middleware is applied too late.
    app.use(csrf());
    ```

    #### Mitigation and Best Practices

    Add the `csurf` middleware *before* you define any routes that modify state. This middleware creates a `req.csrfToken()` function. You must pass this token to your template and include it in your form.

    #### Secure Code Example

    ```javascript theme={null}
    // app.js (Secure Version)
    const express = require('express');
    const csrf = require('csurf'); // Import csurf
    const app = express();

    app.use(require('cookie-session')({ keys: ['secret'] }));
    app.use(express.urlencoded({ extended: false }));
    app.use(csrf()); // SECURE: Use the csurf middleware before routes

    app.get('/profile', (req, res) => {
        // SECURE: Pass the token to the render function
        res.render('profile', { csrfToken: req.csrfToken() });
    });

    app.post('/profile/update', (req, res) => {
        // SECURE: The csurf middleware validates the token automatically.
        req.session.user.email = req.body.email;
        res.redirect('/profile');
    });

    // views/profile.ejs (Secure Version)
    /*
    <form method="POST" action="/profile/update">
        <input type="hidden" name="_csrf" value="<%= csrfToken %>">
        <input type="email" name="email">
        <button type="submit">Update</button>
    </form>
    */
    ```

    #### Testing Strategy

    Use Jest/Supertest. Get the login cookie, then attempt to `POST` to the update endpoint *without* first GETting the page to scrape the token. The request should fail with a `403 Forbidden`.

    ```javascript theme={null}
    // tests/profile.test.js
    it('should fail to POST without a CSRF token', async () => {
        // 'userAgent' is a logged-in supertest agent
        const response = await userAgent
            .post('/profile/update')
            .send({ email: 'new@example.com' });
            
        expect(response.statusCode).toBe(403);
    });
    ```
  </Tab>

  <Tab title="Ruby">
    #### Framework Context

    Rails has `protect_from_forgery with: :exception` in `ApplicationController` by default. This is secure. The vulnerability is a developer adding `skip_before_action :verify_authenticity_token`.

    #### Vulnerable Scenario 1: Skipping the Filter for an Action

    A controller skips the authenticity check for a single action.

    ```ruby theme={null}
    # app/controllers/profile_controller.rb
    class ProfileController < ApplicationController
      before_action :authenticate_user!
      
      # DANGEROUS: Disables CSRF protection for the update action
      skip_before_action :verify_authenticity_token, only: [:update]

      def update
        if request.post?
          current_user.update(email: params[:email])
        end
        redirect_to profile_path
      end
    end
    ```

    #### Vulnerable Scenario 2: Skipping the Filter for a Controller

    A developer, annoyed by CSRF errors during API development, skips the filter for the entire controller.

    ```ruby theme={null}
    # app/controllers/api_controller.rb
    class ApiController < ApplicationController
      # DANGEROUS: If any method in this controller uses session auth,
      # it is now vulnerable to CSRF.
      skip_before_action :verify_authenticity_token
      
      def change_settings
        # ... logic using `current_user` ...
      end
    end
    ```

    #### Mitigation and Best Practices

    Remove the `skip_before_action :verify_authenticity_token` line. Ensure your forms in `.html.erb` files are generated with `form_with` or `form_for`, which automatically include the token. For APIs, use `protect_from_forgery with: :null_session` and use token-based auth.

    #### Secure Code Example

    ```ruby theme={null}
    # app/controllers/profile_controller.rb (Secure Version)
    class ProfileController < ApplicationController
      before_action :authenticate_user!
      
      # SECURE: The 'skip_before_action' is removed.

      def update
        if request.post?
          current_user.update(email: params[:email])
        end
        redirect_to profile_path
      end
    end

    # app/views/profile/show.html.erb (Secure Version)
    /*
    <%= form_with url: profile_update_path, method: :post do |form| %>
      <%= form.email_field :email %>
      <%= form.submit "Update" %>
    <% end %>
    */
    ```

    #### Testing Strategy

    Write an RSpec request spec. Log in as a user. Then, `post` to the update path *without* the `authenticity_token`. This is hard to do in request specs as Rails often adds it. A better test is a controller spec where you explicitly disable token verification to see it fail.

    The easiest way is to test that protection is *on*.

    ```ruby theme={null}
    # spec/controllers/profile_controller_spec.rb
    it 'is protected from forgery' do
      # This checks that the 'protect_from_forgery' is active
      expect(ProfileController).to be_protect_from_forgery
    end
    ```
  </Tab>
</Tabs>
