Back to Blog
Lesson 38 of the PHP: Modern PHP from the Ground Up course
PHPAugust 26, 20263 min read

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.

PHPsecurityCSRFweb developmentbackendtutorial
Close-up of HTML and PHP code on screen showing error message and login form data.

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:

  1. Generate: Create a cryptographically secure, random string and store it in the user's $_SESSION.
  2. Embed: Include this token in a hidden field within your HTML forms.
  3. Validate: When the form is submitted, compare the token in $_POST with 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

  1. Update your BaseController to generate a csrf_token in the session upon initialization.
  2. Create a reusable function (e.g., generateCsrfField()) that returns the hidden HTML input string.
  3. Implement a check in your handlePostRequest logic that validates the token for all POST requests.
  4. 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 use uniqid() or rand().
  • 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.

Similar Posts