Files
boursecu_server/app/Http/Middleware/ValidateRequests.php
T

87 lines
2.2 KiB
PHP

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
class ValidateRequests
{
public function handle(Request $request, Closure $next)
{
//HMAC
$clientId = $request->header('X-Client-Id');
$timestamp = $request->header('X-Timestamp');
$nonce = $request->header('X-Nonce');
$signature = $request->header('X-Signature');
if (!$clientId || !$timestamp || !$nonce || !$signature) {
abort(401, 'Missing HMAC headers');
}
if ($clientId !== config('services.node.client_id')) {
abort(401, 'Invalid client id');
}
/** ✅ اینجا SECRET واقعاً استفاده می‌شود */
$secret = config('services.node.secret');
if (!$secret) {
abort(500, 'Node shared secret not configured');
}
$data = json_decode($request->getContent(), true);
ksort($data);
$normalized = json_encode(
$data,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);
$bodyHash = hash('sha256', $normalized);
// $bodyHash = hash('sha256', $request->getContent() ?? '');
$canonical = implode("\n", [
strtoupper($request->method()),
'/' . ltrim($request->path(), '/'),
$bodyHash,
$timestamp,
$nonce,
]);
$expectedSignature = hash_hmac(
'sha256',
$canonical,
$secret
);
if (!hash_equals($expectedSignature, $signature)) {
abort(401, 'Invalid HMAC signature');
}
// return $next($request);
//ANTI REPLAY
$timestamp = (int)$request->header('X-Timestamp');
$nonce = $request->header('X-Nonce');
if (abs(time() - $timestamp) > 99999999999999999) {
abort(401, 'Expired request');
}
$key = "node_nonce:{$nonce}";
if (Cache::has($key)) {
abort(401, 'Replay attack detected');
}
Cache::put($key, true, 60);
return $next($request);
}
}