nael_d / guardify
Robust CSRF protection for PHP applications with HMAC validation, IP/UA binding, and AJAX support.
Requires
- php: >=7.2.0
README
Guardify is a secure PHP library that prevents CSRF attacks by generating, embedding, and validating tokens to ensure requests come from trusted sources.
Features
- PHP Compatibility: Works with PHP >= 7.2.0 minimum.
- CSRF Token Generation: Generates cryptographically secure CSRF tokens.
- Token Scoping: Supports token scoping for different forms or actions.
- HMAC Validation: Includes optional HMAC validation for enhanced security.
- IP and User-Agent Checks: Provides optional IP and User-Agent checks to prevent token reuse.
- AJAX Support: Facilitates CSRF protection for AJAX requests with
<meta />
tag embedding and validation. - Session and Cookie Storage: Stores tokens in both session and secure cookies with HttpOnly and Strict SameSite attributes.
- Configurable Expiry Time: Allows setting a custom token expiry time.
- Secret Key Requirement: Enforces the use of a secret key for HMAC.
- Current URL Matching: Matches the current URL for validation.
Table of Contents
Installation
Guardify is available to be installed through Composer, basically require it in your project:
composer require nael_d/guardify
You are also able to download Guardify manually. Download Guardify and require src/Guardify.php
.
For a better release checking, we may suggest to review Releases page to pick up and review developing timeline, as it provides a clear listing of versions along with detailed descriptions of each release.
Getting Started
Once installed, the class Guardify
is ready for you.
💡
Guardify
class is available underGuardify
namespace.
To use Guardify, you must first set a secret key using Guardify::setSecretKey($yourSecretKey)
. This key is essential for HMAC validation and security.
// Initialize in bootstrap file
\Guardify\Guardify::setSecretKey('your-secret-key-here');
Properties
While Guardify's properties are private, understanding their purpose can provide valuable insights into the library's functionality.
$length:
Default is
int 32
. The length of the generated token in bytes.$expiry_time:
Default value is
int 60
. The token expiry time in seconds.$secret_key:
Required, type is
string
. The secret key used for HMAC validation.$enable_ip_check:
Default is
bool true
. A boolean indicating whether to enable IP address checking.$enable_ua_check:
Default is
bool true
. A boolean indicating whether to enable User-Agent checking.$enable_hmac:
Default is
bool true
. A boolean indicating whether to enable HMAC validation.$ajax_auto_regenerate:
Default is
bool true
. A boolean indicating whether to auto-regenerating AJAX tokens after validation.
Methods
Guardify offers methods for generating, embedding, and validating CSRF tokens.
setSecretKey(string $key):
Sets the secret key for HMAC validation.
Parameters:
string $key
: The secret key.
💡 Dashes
-
and Underscores_
are only allowed to avoid unexpected conflicts.Usage:
\Guardify\Guardify::setSecretKey('your_strong_secret_key');
getExpiryTime():
Retrieves the current token expiry time.
Usage:
echo \Guardify\Guardify::getExpiryTime(); // outputs: 60
setExpiryTime(int $time):
Sets the token expiry time.
Parameters:
int $time
: The expiry time in seconds.
Usage:
\Guardify\Guardify::setExpiryTime(120);
enableIpCheck(bool $enabled):
Enables or disables IP address validation (disable for proxy/CDN setups).
Parameters:
bool $enabled
: Default istrue
,true
to enable,false
to disable.
Usage:
\Guardify\Guardify::enableIpCheck(false); // ================= OR ================= \Guardify\Guardify::enableIpCheck(true);
enableUaCheck(bool $enabled = true):
Enables or disables User-Agent checking.
Parameters:
bool $enabled
: Default istrue
,true
to enable,false
to disable.
Usage:
\Guardify\Guardify::enableUaCheck(false); // ================= OR ================= \Guardify\Guardify::enableUaCheck(true);
enableHmac(bool $enabled = true):
Enables or disables User-Agent checking.
Parameters:
bool $enabled
: Default istrue
,true
to enable,false
to disable.
Usage:
\Guardify\Guardify::enableHmac(false); // ================= OR ================= \Guardify\Guardify::enableHmac(true);
enableAutoRegenerateAjax(bool $enabled = true):
Enables or disables regenerating AJAX tokens automatically after validation.
Parameters:
bool $enabled
: Default istrue
,true
to enable,false
to disable.
Usage:
\Guardify\Guardify::enableAutoRegenerateAjax(false); // ================= OR ================= \Guardify\Guardify::enableAutoRegenerateAjax(true);
isIpCheckEnabled():
Retrieves the current status of IP address validation.
Usage:
\Guardify\Guardify::isIpCheckEnabled(); // > true => enabled | > false => disabled
isUaCheckEnabled():
Retrieves the current status of User-Agent validation.
Usage:
\Guardify\Guardify::isUaCheckEnabled(); // > true => enabled | > false => disabled
isHmacEnabled():
Retrieves the current status of HMAC validation.
Usage:
\Guardify\Guardify::isHmacEnabled(); // > true => enabled | > false => disabled
embed(string $scope = 'global'):
Embeds a CSRF token as a hidden input field.
Parameters:
string $name
: Default is'global'
. The scope of the token's namespace.
Usage:
\Guardify\Guardify::embed(); // outputs: <input type="hidden" name="csrf_UNIQUE_SCOPE_TOKEN" value="scope-specific token" /> \Guardify\Guardify::embed('custom_form'); // outputs: <input type="hidden" name="csrf_UNIQUE_SCOPE_TOKEN" value="scope-specific token" /> // The scope 'custom_form' is stored in the core of that current request, // allowing multiple forms in the same webpage.
embedAjaxMeta(string $scope):
Embeds a CSRF token as a meta tag for AJAX requests.
Parameters:
string $scope
: Required. The scope of the token's namespace.
Usage:
\Guardify\Guardify::embedAjaxMeta('csrf_comment_form'); // outputs: <meta name="csrf-token-csrf_comment_form" content="scope-specific token" />
getTokenKey(string $scope = null):
Returns the
<input name="" />
to be used in CSRF validation.Parameters:
string $scope
: Default isnull
. Defines a unique namespace for your CSRF token, allowing you to isolate tokens for different forms or endpoints.
Usage:
echo \Guardify\Guardify::getTokenKey(); // null => 'global' default namespace. // csrf_global echo \Guardify\Guardify::getTokenKey('new_post_form'); // csrf_UNIQUE_SCOPE_TOKEN
Using
'global'
namespace scope through multiple forms or endpoints will work only for any first performed request before getting expired.validateForm($token, $scope = 'global'):
Validates form submissions for that specified namespace scope.
Parameters:
string $token
: Required. The token value relates to that specified namespace scope.string $scope
: Default is'global'
. The scope of the token's namespace.
To retrieve the correct token value when using custom scopes, note that each token generates a unique input name following the pattern
<input name="csrf_UNIQUE_SCOPE_TOKEN" />
. For reliable access, you may use thegetTokenKey($scope)
method to obtain the exact input name matching your scope.Usage:
<!-- [HTML] --> <h3>Cpanel login</h3> <hr /> <form method="post" action="/endpoint"> <input type="text" name="username" placeholder="Username" required /> <input type="password" name="password" placeholder="Password" required /> <?php echo \Guardify\Guardify::embed('cpanel_login_form'); ?> <button type="submit" name="login" value="login">Login</button> </form>
// [PHP] - received `/endpoint` POST request: // Scope namespace $scope = 'cpanel_login_form'; // Getting the CSRF input's name for that scope namespace: $name = \Guardify\Guardify::getTokenKey($scope); if (\Guardify\Guardify::validateForm($_POST[$name], $scope)) { // Token is valid, proceed with the request. } else { // Token is invalid, handle the error. }
validateAjax($scope = 'global'):
Validates AJAX requests for that specified namespace scope.
Parameters:
string $scope
: Default is'global'
. The scope of the token's namespace.
Instead of conveying the token in the POST request's content, utilize Headers for its transmission.
Usage:
<!-- [HTML] --> <head> <?php echo \Guardify\Guardify::embedAjaxMeta('ajax_form'); ?> <!-- <meta name="csrf-token-ajax_form" content="scope-specific token"> --> </head> <h3>Cpanel AJAX login</h3> <hr /> <form method="post" action="/endpoint"> <input type="text" name="username" placeholder="Username" required /> <input type="password" name="password" placeholder="Password" required /> <button type="submit" name="login" value="login">Login</button> </form>
// Javascript let scope = `ajax_form`; document.getElementsByTagName('form')[0].addEventListener('submit', e => { e.preventDefault(); // Send POST request using fetch() fetch(location.href, { method: 'POST', headers: { // Transmit the token using 'X-CSRF-TOKEN' header key. 'X-CSRF-TOKEN': document.querySelector(`meta[name="csrf-token-${scope}"]`).content, }, }) .then(data => { if (data.headers.get('X-CSRF-TOKEN')) { // Set the new regenerated token for the next request (if auto-regeneration is enabled). document.querySelector(`meta[name="csrf-token-${scope}"]`).content = data.headers.get('X-CSRF-TOKEN'); } }) });
// [PHP] - received `/endpoint` POST request: // Scope namespace $scope = 'ajax_form'; \Guardify\Guardify::enableAutoRegenerateAjax(true); // Default is auto-regenerate. if (\Guardify\Guardify::validateAjax($scope)) { // Token is valid, proceed with the request. } else { // Token is invalid, handle the error. }
Troubleshooting
If you're experiencing issues, check the following:
Tokens expire unexpectedly:
Ensure extending expiration time more than the default preset value
60
usingsetExpiryTime()
.Validation fails behind proxies:
Disable IP checking:
\Guardify\Guardify::enableIpCheck(false);
.HMAC validation errors:
Confirm the secret key is set:
\Guardify\Guardify::setSecretKey('your-key');
.
Contributing
Guardify is an open-source project and contributions are welcome! To contribute, please fork the repository, create a new branch, and submit a pull request with your changes.
License
This project is licensed under the Mozilla Public License 2.0 (MPL-2.0). You are free to use, modify, and distribute this library under the terms of the MPL-2.0. See the LICENSE file for details.
© Made with ❤️ by Nael Dahman