
CSRF
Cross-Site Request Forgery (CSRF) - A Developer’s Guide
What is CSRF?
Cross-Site Request Forgery (CSRF) is a web security vulnerability that tricks a user into executing unwanted actions on a trusted website where they are authenticated.
Example Attack Scenario:
- A user logs into
bank.com
and their session is authenticated. - They visit a malicious site while still logged into
bank.com
. - The malicious site submits a forged request to
bank.com
, triggering actions like transferring money. - Since the user is already logged in, the request is processed as if the user intended it.
How CSRF Works
Example of a CSRF Attack
Let’s say a banking website allows users to transfer money via a simple GET request:
<a href="https://bank.com/transfer?amount=1000&to=attacker_account">Click Here!</a>
If a logged-in user clicks the link, their browser automatically sends their session cookies to bank.com
, completing the transfer without their consent.
CSRF Protection Techniques
To prevent CSRF attacks, websites must verify that requests come from a trusted source.
1. CSRF Tokens (Best Practice)
A CSRF token is a random, unique string generated per session or request. The server validates this token before processing actions.
Implementation in Node.js (Express)
Install csurf
middleware:
npm install csurf
Apply it to your Express app:
const csrf = require("csurf");
const cookieParser = require("cookie-parser");
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.get("/form", (req, res) => {
res.send(`<form method="POST" action="/transfer">
<input type="hidden" name="_csrf" value="${req.csrfToken()}">
<input type="text" name="amount" placeholder="Enter Amount">
<button type="submit">Transfer</button>
</form>`);
});
app.post("/transfer", (req, res) => {
// Only processes if valid _csrf token is sent
res.send("Transfer successful");
});
Why It Works?
- CSRF tokens are tied to the user’s session.
- Attackers cannot guess or reuse tokens.
2. SameSite Cookies
SameSite prevents cookies from being sent with cross-origin requests.
Set SameSite=Strict
in Cookies
app.use(session({
secret: "your_secret",
cookie: { sameSite: "Strict", httpOnly: true }
}));
Strict
: Cookies only sent for same-site requests (Best for security).Lax
: Cookies sent for top-level navigations only.None
: Cookies sent for all requests (Requires HTTPS).
3. Verifying the Referrer / Origin
Check if requests come from an expected origin:
app.post("/transfer", (req, res) => {
const origin = req.get("Origin");
if (origin !== "https://bank.com") {
return res.status(403).send("Forbidden");
}
res.send("Transfer successful");
});
Downside: Not all browsers send the Origin
header in same-site requests.
4. Disable GET
for State-Changing Actions
Avoid making sensitive actions accessible via GET requests. Use POST
or PUT
instead.
Bad:
<a href="https://bank.com/transfer?amount=1000&to=attacker">Transfer Money</a>
Good:
<form method="POST" action="/transfer">
<input type="hidden" name="amount" value="1000">
<button type="submit">Transfer</button>
</form>
CSRF vs. XSS
Attack Type | CSRF | XSS (Cross-Site Scripting) |
---|---|---|
Exploits user’s authentication | ✅ Yes | ❌ No |
Injects malicious script into a website | ❌ No | ✅ Yes |
Requires victim to be logged in | ✅ Yes | ❌ No |
Prevented by CSRF tokens | ✅ Yes | ❌ No |
Prevented by input validation | ❌ No | ✅ Yes |
Testing for CSRF (Manual & Automated)
Manual Testing
- Identify state-changing requests (e.g., money transfer, password change).
- Try executing them from an unauthorized source (e.g., another website).
- Check if the action succeeds without authentication validation.
Automated Testing
Use tools like:
- OWASP ZAP (Zed Attack Proxy)
- Burp Suite
- Postman (For testing request behavior)
Real-World CSRF Attack Example
2019: GitHub CSRF Vulnerability
- GitHub had a CSRF issue where attackers could trick users into adding their SSH keys.
- GitHub quickly patched it by enforcing CSRF tokens.
Conclusion
CSRF is dangerous but preventable using:
- CSRF tokens (Best approach)
- SameSite cookies
- Checking Origin headers
- Avoiding GET for state-changing requests
Always test your web applications for CSRF vulnerabilities to prevent unauthorized actions.