config_preview_deploy-1.0.0-alpha3/src/HashVerification.php
src/HashVerification.php
<?php
declare(strict_types=1);
namespace Drupal\config_preview_deploy;
use Drupal\Core\Site\Settings;
use Drupal\Core\Url;
/**
* Provides hash generation and verification for secure communication.
*
* This service centralizes all hash-based verification logic using a
* production-host + time-based approach to ensure secure communication
* between preview and production environments.
*/
class HashVerification {
/**
* The settings service.
*
* @var \Drupal\Core\Site\Settings
*/
protected Settings $settings;
/**
* Constructs a HashVerification object.
*
* @param \Drupal\Core\Site\Settings $settings
* The settings service.
*/
public function __construct(Settings $settings) {
$this->settings = $settings;
}
/**
* Generates a verification hash for secure communication.
*
* @param string $productionHost
* The production host to bind the hash to.
* @param int $timestamp
* The timestamp for the hash.
*
* @return string
* The generated verification hash.
*/
public function generateVerificationHash(string $productionHost, int $timestamp): string {
return hash('sha256', $productionHost . $timestamp . $this->getHashSalt());
}
/**
* Verifies a provided hash against expected values.
*
* @param string $providedHash
* The hash to verify.
* @param int $timestamp
* The timestamp to verify.
* @param string|null $productionHost
* (optional) The production host to verify against. If not provided,
* extracts the host from Drupal's base URL.
*
* @return bool
* TRUE if the hash is valid, FALSE otherwise.
*/
public function verifyHash(string $providedHash, int $timestamp, ?string $productionHost = NULL): bool {
// Check timestamp age to prevent replay attacks (fixed 300 seconds).
if (abs(time() - $timestamp) > 300) {
return FALSE;
}
// If production host not provided, extract from Drupal's base URL.
if ($productionHost === NULL) {
// Get current site's base URL using admin path to avoid frontend domain
// overrides.
$currentUrl = Url::fromRoute('system.admin', [], ['absolute' => TRUE])->toString();
$productionHost = parse_url($currentUrl, PHP_URL_HOST);
}
$expectedHash = $this->generateVerificationHash($productionHost, $timestamp);
return hash_equals($expectedHash, $providedHash);
}
/**
* Gets the hash salt from Drupal settings.
*
* @return string
* The hash salt.
*/
protected function getHashSalt(): string {
return $this->settings->getHashSalt();
}
}
