Protecting Against CSRF: Secure Your PHP Forms
Learn to defend your PHP applications against CSRF attacks. Discover how to generate, embed, and validate CSRF tokens to keep your users' data safe.

Previously in this course, we covered Managing State with Superglobals, which introduced the $_SESSION array. In this lesson, we build upon that foundation to implement CSRF protection, ensuring that the requests sent to your server are intentional and authorized.
Understanding CSRF: The First Principles
Cross-Site Request Forgery (CSRF) is an attack where a malicious site tricks a user's browser into performing an action on a different website where the user is currently authenticated. Because browsers automatically include cookies (like your session ID) with every request to the originating domain, your server assumes the request is legitimate.
If you don't verify that a request was intentionally initiated by your own UI, an attacker can force your logged-in users to change their passwords, update profile details, or perform financial transactions without their knowledge.
Generating and Validating CSRF Tokens
To prevent this, we use a "Synchronizer Token Pattern." The strategy is simple:
- Generate: Create a cryptographically secure, random string and store it in the user's
$_SESSION. - Embed: Include this token in a hidden field within your HTML forms.
- Validate: When the form is submitted, compare the token in
$_POSTwith the one stored in$_SESSION. If they don't match, reject the request.
Worked Example: Securing a Form
First, ensure your session is started. In your controller or entry point, generate a token if one doesn't exist:
PHP#6A9955">// In a helper or base controller session_start(); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); }
Next, include this token in your view template inside the <form> element:
HTML<!-- Inside your form --> style="color:#808080"><style="color:#4EC9B0">input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
Finally, validate the token upon receiving a POST request:
PHP#6A9955">// Inside your controller's POST handler if ($_SERVER['REQUEST_METHOD'] === 'POST') { $userToken = $_POST['csrf_token'] ?? ''; if (!hash_equals($_SESSION['csrf_token'], $userToken)) { #6A9955">// Token mismatch: potential attack http_response_code(403); die('CSRF token validation failed.'); } #6A9955">// Proceed with processing form data... }
Note: We use hash_equals() instead of == to prevent timing attacks, which is a best practice for comparing sensitive strings.
Hands-on Exercise
- Update your
BaseControllerto generate acsrf_tokenin the session upon initialization. - Create a reusable function (e.g.,
generateCsrfField()) that returns the hidden HTML input string. - Implement a check in your
handlePostRequestlogic that validates the token for allPOSTrequests. - Test it by intentionally passing a wrong token in the HTML and observing the 403 error.
Common Pitfalls to Avoid
- Including tokens in GET requests: Never do this. CSRF tokens should only protect state-changing requests (POST, PUT, DELETE). GET requests should never modify data.
- Using predictable tokens: Always use
random_bytes()or a cryptographically secure pseudo-random number generator. Never useuniqid()orrand(). - Forgetting session start: If
session_start()isn't called before accessing$_SESSION, your token validation will fail every time. - Hardcoding tokens: Never hardcode tokens in your code; they must be generated per-session or per-request to remain effective.
FAQ
Does this make my app 100% secure? CSRF protection is one piece of the puzzle. You still need to manage input sanitization and validation, as discussed in Sanitization and Validation.
Can I use the same token for multiple forms? Yes, but refreshing it periodically or per-session is safer. Some frameworks rotate the token on every request, but a per-session token is generally sufficient for beginner-level MVC apps.
What happens if the user opens two tabs? If you store one token in the session, both tabs will share it. This works fine unless you implement strict per-form-instance tokens.
Recap
CSRF protection turns your application from a "blind follower" of requests into a gatekeeper that validates the origin of every action. By generating a secure token, embedding it in your forms, and verifying it using hash_equals during processing, you neutralize the most common form of cross-site forgery.
Up next
In the next lesson, we will move toward professional-grade architecture by implementing Dependency Injection to decouple our controllers from their service dependencies.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

FilamentPHP Admin Panel & Dashboard Development
A powerful admin panel for your Laravel app — built with FilamentPHP so you can manage everything without touching the database.
