Skip to content

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Web-Sicherheit Glossary

CSRF (Cross-Site Request Forgery)

Cross-Site Request Forgery (CSRF) is an attack in which an attacker tricks a logged-in user's browser into sending unintended HTTP requests to a web application—on the user's behalf and without their knowledge. It is listed among the OWASP Top 10 and is particularly dangerous in the context of state-changing actions.

CSRF attacks exploit the trust established by browser sessions: Cookies are automatically sent along—even if the request comes from a third-party website. A single click on a malicious link can change account settings, trigger transactions, or take over accounts.

How CSRF Works

The attack scenario for a money transfer involves six steps:

  1. The victim is logged in to bank.de (session cookie present).
  2. The attacker creates a malicious webpage at evil.com/malicious.html.
  3. The victim clicks on a link to evil.com, e.g., via a phishing email.
  4. evil.com loads either a hidden image tag (<img src="https://bank.de/transfer?amount=1000&to=attacker" />) or an automatically submitted hidden form.
  5. The browser sends the request to bank.dewith the victim’s session cookie—automatically and without the victim’s knowledge.
  6. bank.de considers the request a legitimate user request and executes the transfer.

Conditions for CSRF

  • The victim must be logged in (session cookie valid)
  • The application does not verify whether the request originates from its own site
  • The attacker knows the target URL (often easy to guess)
  • No CSRF protection measures are implemented

CSRF vs. XSS - Key Difference

PropertyXSSCSRF
Attack vectorAttacker injects JavaScript into the target websiteAttacker tricks the browser into sending a request
ControlAttacker controls code within the context of the target siteAttacker does not see the response (Same-Origin Policy)
Dangerous ActionsSteal cookies, intercept form dataOnly state-changing actions (POST/PUT/DELETE)
CSRF ProtectionBypasses CSRF protection (code from the same origin)GET requests that modify data are particularly vulnerable

> Mnemonic: XSS allows attackers to read and manipulate browser data. CSRF tricks the browser into performing actions on behalf of the user.

CSRF Protection Measures

1. CSRF Tokens (Synchronizer Token Pattern) - Primary Measure

The server generates a random token, stores it in the session, and embeds it in every form. For every state-changing request, the server checks whether the token sent along matches the session token.

<form action="/transfer" method="POST">
  <input type="hidden" name="_csrf" value="a9d3e7b2c4f1..." />
  <input name="amount" />
  <button>Transfer</button>
</form>```

```typescript
// Express.js with csurf:
import csrf from &#x27;csurf&#x27;;
const csrfProtection = csrf({ cookie: false });  // Session-based!

app.get(&#x27;/transfer&#x27;, csrfProtection, (req, res) =&gt; {
  res.render(&#x27;transfer&#x27;, { csrfToken: req.csrfToken() });
});

app.post(&#x27;/transfer&#x27;, csrfProtection, (req, res) =&gt; {
  // csurf checks the token automatically!
  // If token is missing/incorrect → 403 Forbidden
});

The SameSite attribute controls when cookies are sent along with cross-site requests:

ValueBehaviorUsage
StrictCookie is never sent with cross-site requests - absolute CSRF protectionProblematic: Users arriving via external links are not logged in
LaxCookie only sent with top-level navigation using GETRecommended for most apps; protects against CSRF forms (POST), allows normal external links
None; SecureCookie sent with all cross-site requests – only with HTTPSOnly for CORS APIs that require cookies (e.g., widgets)
Set-Cookie: session=abc; SameSite=Lax; Secure; HttpOnly; Path=/

> Note: Since Chrome 80, SameSite=Lax is the default if no SameSite attribute is set.

As an alternative when server-side sessions are not available:

  1. Server sets CSRF token as a cookie (not httpOnly!)
  2. JavaScript reads the cookie and sends the token in the request header
  3. Server checks: Cookie token == header token?

Advantage: Works without a server-side session. Disadvantage: Vulnerable to subdomain attacks (XSS on subdomain.company.com can read and manipulate the cookie).

4. Custom Request Header (for AJAX/APIs)

APIs that accept only JSON can use the following header checks:

  • request.headers[&#x27;X-Requested-With&#x27;] == &#x27;XMLHttpRequest&#x27;
  • request.headers[&#x27;Content-Type&#x27;] == &#x27;application/json&#x27;

Normal browser forms cannot send custom headers—this check therefore functions as implicit CSRF protection. Note: Use only for APIs, not for HTML forms.

Vulnerable HTTP Methods

GET requests should never be state-changing (REST principle). They are particularly vulnerable because browsers, crawlers, and proxies cache and automatically execute GETs. An example of a critical vulnerability: <img src="https://example.com/delete-account">.

POST, PUT, and DELETE requests are the primary targets of CSRF and must be protected with tokens or SameSite.

Exceptions (no CSRF protection required):

  • Read-only GET requests without state changes
  • Endpoints used exclusively via API (no browser cookie)
  • OAuth2 token endpoints (no cookie) — but: implement a state parameter as CSRF protection

CSRF in Modern SPAs

Single Page Applications (React, Vue, Angular) send API requests via fetch or axios, not through HTML forms. Since custom headers can be set, there is natural CSRF protection for API calls. However, cookies must still be protected.

Modern Setup for SPAs

Backend (Express) with JWT in the Authorization header:

// JWT in the Authorization header: NO CSRF issue!
// Cross-site forms cannot send an Authorization header.

// Session/Cookie Auth: SameSite=Lax is usually sufficient:
res.cookie(&#x27;session&#x27;, token, {
  httpOnly: true,
  secure: true,
  sameSite: &#x27;lax&#x27;
});

Frontend (React) with JWT:

// Authorization Header (JWT): no cookie → no CSRF
fetch(&#x27;/api/transfer&#x27;, {
  method: &#x27;POST&#x27;,
  headers: {
    &#x27;Authorization&#x27;: `Bearer ${accessToken}`,
    &#x27;Content-Type&#x27;: &#x27;application/json&#x27;,
  },
  body: JSON.stringify({ amount: 100 }),
});

// Cookie-based authentication with CSRF token:
fetch(&#x27;/api/transfer&#x27;, {
  method: &#x27;POST&#x27;,
  credentials: &#x27;include&#x27;,          // Send cookie
  headers: {
    &#x27;X-CSRF-Token&#x27;: csrfToken,     // Token from meta tag or API
    &#x27;Content-Type&#x27;: &#x27;application/json&#x27;,
  },
});

Detecting CSRF in Penetration Tests

Manual CSRF Tests

  1. Identify state-changing endpoints: Log all POST/PUT/DELETE requests with Burp Suite, remove the CSRF token, and check whether the request is still successful.

  2. Check CSRF token quality:

    • Is the token random or predictable?
    • Is the token valid per session or per request?
    • Is the token included in the URL? (URL is logged → token leakage)
    • Is the token too short? (less than 128 bits of entropy)
  3. Check SameSite cookie: curl -v https://example.com/login and check the Set-Cookie header for SameSite.

  4. Check CORS headers: Access-Control-Allow-Origin: * combined with Access-Control-Allow-Credentials: true is a CORS misconfiguration.

  5. Referer header check: Send requests without a Referer header and check if they are accepted. This is a weak measure, as the Referer can be forged.

Automated Tools

  • Burp Suite: CSRF PoC Generator
  • OWASP ZAP: CSRF Scanner
  • csrf-poc-generator (npm): automatic PoC generation

Example: CSRF PoC for Reporting

<!-- CSRF Proof-of-Concept (für Pentest-Reports) -->
<!DOCTYPE html>
<html>
<head><title>CSRF PoC</title></head>
<body>
  <h1>CSRF Test - Automatic POST to bank.de</h1>
  <form id="csrf-form"
        action="https://bank.de/api/transfer"
        method="POST">
    <input type="hidden" name="amount" value="1000" />
    <input type="hidden" name="recipient" value="IBAN_ANGREIFER" />
    <input type="hidden" name="note" value="CSRF-Test" />
  </form>
  <script>
    // Automatisches Absenden ohne Nutzerinteraktion
    document.getElementById(&#x27;csrf-form&#x27;).submit();
  </script>
</body>
</html>```

### CVSS Rating (Example: CSRF on Bank Transfer)

| Metric | Value |
|---|---|
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | None |
| User Interaction | Required |
| Scope | Unchanged |
| Confidentiality | None |
| Integrity | High |
| Availability | None |
| **CVSS Base Score** | **6.5 (Medium)** |

The actual severity rating is high if state-changing actions are involved, such as password changes, granting admin privileges, or data deletion.