fix: PSR-4 compliance — rename core/middleware/services to PascalCase for Linux server compatibility
This commit is contained in:
62
app/Middleware/HmacMiddleware.php
Normal file
62
app/Middleware/HmacMiddleware.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* HMAC Request Signature Middleware
|
||||
*
|
||||
* Verifies that incoming requests are signed with a shared secret,
|
||||
* preventing replay attacks and ensuring request integrity.
|
||||
*
|
||||
* Client must send:
|
||||
* X-Timestamp: Unix timestamp (seconds)
|
||||
* X-HMAC-Signature: HMAC-SHA256(timestamp + "." + raw_body, HMAC_SECRET_KEY)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Middleware;
|
||||
|
||||
use App\Core\Security;
|
||||
|
||||
final class HmacMiddleware
|
||||
{
|
||||
/**
|
||||
* @param int $maxAgeSeconds Max age for replay attack window (default: 5 minutes)
|
||||
*/
|
||||
public static function verify(int $maxAgeSeconds = 300): void
|
||||
{
|
||||
$headers = getallheaders();
|
||||
$signature = $headers['X-HMAC-Signature'] ?? $headers['x-hmac-signature'] ?? '';
|
||||
$timestamp = $headers['X-Timestamp'] ?? $headers['x-timestamp'] ?? '';
|
||||
|
||||
// 1. Ensure both headers are present
|
||||
if (empty($signature) || empty($timestamp)) {
|
||||
json_error('Missing HMAC signature or timestamp', 401);
|
||||
}
|
||||
|
||||
// 2. Validate timestamp is numeric
|
||||
if (!ctype_digit((string)$timestamp)) {
|
||||
json_error('Invalid timestamp format', 401);
|
||||
}
|
||||
|
||||
// 3. Replay attack prevention — reject stale requests
|
||||
$age = abs(time() - (int)$timestamp);
|
||||
if ($age > $maxAgeSeconds) {
|
||||
json_error('Request expired. Check your system clock.', 401);
|
||||
}
|
||||
|
||||
// 4. Build the expected signature
|
||||
$body = file_get_contents('php://input');
|
||||
$payload = $timestamp . '.' . $body;
|
||||
$secret = env('HMAC_SECRET_KEY');
|
||||
|
||||
if (!$secret || strlen($secret) < 32) {
|
||||
error_log('FATAL: HMAC_SECRET_KEY is missing or too short in .env');
|
||||
json_error('Server configuration error', 500);
|
||||
}
|
||||
|
||||
// 5. Verify using constant-time comparison (prevents timing attacks)
|
||||
if (!Security::verifySignature($payload, $signature, $secret)) {
|
||||
error_log("HMAC verification failed for " . ($_SERVER['REQUEST_URI'] ?? ''));
|
||||
json_error('Invalid request signature', 401);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user