CSRF Token Validator
Validates a submitted CSRF token against the stored session token and returns a clear pass/fail result.
Function signature
ogValidateCsrfToken(submitted_token, stored_token_record, scope)
Categories
- Security
Parameters
submitted_tokenToken received from a POST form.stored_token_recordStored token metadata. Recognized keys: `expires_at`, `scope`, `token`, `token_hash`.scopeExpected scope name.Return value
Short public-safe status message.
- valid
- scope
- should_rotate
Compatibility
Existing function name and call order preserved; metadata signature corrected to source.
Minimum PHP version: 7.4
Security notes
Validate request method, identity, permissions, and caller-owned allowlists before use; keep secrets out of public output.
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Validates a submitted CSRF token against a stored scoped token record.
*
* The stored record may contain either the raw token or a SHA-256 token_hash.
* Hash storage is preferred so accidental session dumps do not expose the raw
* browser token.
*
* @param string $submitted_token Token received from a POST form.
* @param array $stored_token_record Stored token metadata.
* @param string $scope Expected scope name.
* @return array Structured validation result.
*/
function ogValidateCsrfToken($submitted_token, $stored_token_record, $scope) {
$result = array(
'success' => false,
'message' => '',
'data' => array('valid' => false)
);
$submitted_token = trim((string)$submitted_token);
$scope = trim((string)$scope);
if (empty($submitted_token)) {
$result['message'] = 'Missing submitted CSRF token.';
return $result;
}
if (!is_array($stored_token_record)) {
$result['message'] = 'Stored CSRF token record is invalid.';
return $result;
}
if (empty($scope)) {
$result['message'] = 'Missing CSRF scope.';
return $result;
}
if (empty($stored_token_record['scope']) || !hash_equals((string)$stored_token_record['scope'], $scope)) {
$result['message'] = 'CSRF scope mismatch.';
return $result;
}
if (empty($stored_token_record['expires_at'])) {
$result['message'] = 'CSRF token expiration is missing.';
return $result;
}
if ((int)$stored_token_record['expires_at'] < time()) {
$result['message'] = 'CSRF token expired.';
return $result;
}
$submitted_hash = hash('sha256', $submitted_token);
$valid = false;
if (!empty($stored_token_record['token_hash'])) {
if (hash_equals((string)$stored_token_record['token_hash'], $submitted_hash)) {
$valid = true;
}
} elseif (!empty($stored_token_record['token'])) {
if (hash_equals((string)$stored_token_record['token'], $submitted_token)) {
$valid = true;
}
}
if (!$valid) {
$result['message'] = 'CSRF token mismatch.';
return $result;
}
$result['success'] = true;
$result['message'] = 'CSRF token validated.';
$result['data'] = array(
'valid' => true,
'scope' => $scope,
'should_rotate' => true
);
return $result;
}