Building on our previous discussion of simple CSRF tokens, this post will focus on implementing per-form CSRF tokens in PHP. This approach enhances security by generating a unique token for each form, reducing the risk of token reuse and potential attacks.
Why Use Per-Form CSRF Tokens?
Per-form CSRF tokens provide an additional layer of security by ensuring that each form submission is uniquely validated. This method is particularly effective in mitigating the impact of token leakage, as an attacker would only be able to exploit a specific form or action.
Generating Per-Form CSRF Tokens
To implement per-form tokens, we need to generate a unique token for each form and store it in the session with a unique identifier. Here’s how you can achieve this:
1. Token Generation and Storage
For each form, generate a secure token and store it in the session with a specific key associated with the form:
session_start();
function generateFormToken($formName) {
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_tokens'][$formName] = $token;
return $token;
}
$formName = 'contact_form';
$token = generateFormToken($formName);
2. Embedding the Token in Forms
Include the generated token in the form as a hidden input field. This ensures that the token is sent along with the form data:
<form method="post" action="process_form.php">
<!-- Other form fields -->
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($token); ?>">
<input type="hidden" name="form_name" value="<?php echo htmlspecialchars($formName); ?>">
<input type="submit" value="Submit">
</form>
3. Validating the Token
Upon form submission, validate the token by checking it against the session-stored token for the specific form:
session_start();
function validateFormToken($formName, $receivedToken) {
if (isset($_SESSION['csrf_tokens'][$formName]) && hash_equals($_SESSION['csrf_tokens'][$formName], $receivedToken)) {
unset($_SESSION['csrf_tokens'][$formName]); // Invalidate the token after use
return true;
}
return false;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$formName = $_POST['form_name'];
$receivedToken = $_POST['csrf_token'];
if (validateFormToken($formName, $receivedToken)) {
// Proceed with the action
} else {
// Handle invalid token
echo "Invalid or expired token.";
}
}
Advantages of Per-Form Tokens
Enhanced Security: Each form has its own token, minimizing the risk of token reuse.
Flexibility: Allows handling multiple forms on the same page with distinct tokens.
Reduced Impact of Token Leakage: Limits potential exploitation to a specific form or action.
Best Practices
Session Management: Ensure secure session handling to prevent session hijacking.
HTTPS: Use HTTPS to protect token transmission.
Token Expiration: Consider implementing token expiration to further enhance security.
By implementing per-form CSRF tokens, you can significantly bolster the security of your PHP applications, ensuring that each form submission is uniquely validated and protected against potential attacks.