TL;DR
Anti-CSRF tokens are much more reliable than relying on the Referer header or simple POST request checks to prevent Cross-Site Request Forgery (CSRF) attacks. This guide explains why and how to implement token-based protection.
Understanding CSRF
CSRF lets an attacker trick a logged-in user into performing unwanted actions on a web application they’re currently using. Imagine you’re logged into your bank, and an attacker sends you a malicious link or gets you to visit a compromised website. That site could then make requests *as if* it were you, potentially transferring money without your knowledge.
Why Referer Checks Aren’t Enough
The Referer header tells the server where the request came from. It seems like a good idea to only accept POST requests that originate from your own site. However, this is easily bypassed:
Browser Settings: Users can disable sending the Referer header.
Proxy Servers: Proxies can modify or remove the Referer header.
JavaScript: JavaScript can initiate requests without a Referer header, or spoof it.
Therefore, relying solely on Referer checks is insecure.
Why Simple POST Testing Isn’t Enough
Checking if a POST request was made *at all* doesn’t prevent CSRF. An attacker can still send a valid POST request; they just need to get the user to trigger it. This isn’t protection, it’s simply knowing a request happened.
Implementing Anti-CSRF Tokens: The Right Way
Anti-CSRF tokens add a secret, unique value to each form submission that the server validates. Here’s how:
1. Token Generation
When a user loads a page with a form (e.g., changing their password), generate a random, cryptographically secure token on the *server-side*. Don’t use predictable values!
# Example Python using secrets module
import secrets
token = secrets.token_hex(16)
2. Token Inclusion in Forms
Embed this token as a hidden field within your form.
<form action=”/change-password” method=”post”>
<input type=”hidden” name=”csrf_token” value=”{{ csrf_token }}”>
… other form fields …
</form>
3. Token Storage (Server-Side)
Store the token associated with the user’s session on the server. Common methods include:
Session Variables: The simplest approach, but can be limited by session storage mechanisms.
Database: More robust for larger applications and scalability.
4. Token Validation
When the form is submitted:
Retrieve the token from the form submission.
Retrieve the stored token for that user’s session.
Compare the tokens. If they match, the request is likely legitimate. If not, reject it.
# Example Python (simplified)
if request.method == ‘POST’:
submitted_token = request.form[‘csrf_token’]
stored_token = session.get(‘csrf_token’)
if submitted_token == stored_token:
# Process the form data
…
else:
# Reject the request – CSRF detected!
abort(403)
5. Token Rotation
For extra security, regenerate the token after each successful submission or periodically (e.g., every hour). This limits the window of opportunity for an attacker if a token is compromised.
Important Considerations
HTTPS: Always use HTTPS to protect tokens in transit.
Token Length: Use sufficiently long and random tokens (at least 32 characters).
Framework Support: Most web frameworks (Django, Flask, Ruby on Rails, Laravel, etc.) provide built-in CSRF protection mechanisms – use them! They handle token generation, storage, and validation for you.
The post CSRF Protection: Tokens vs Referer Checks appeared first on Blog | G5 Cyber Security.
No Responses