89 lines
2.2 KiB
PHP
89 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
use App\Services\ConfigService;
|
|
use Closure;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
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');
|
|
}
|
|
|
|
$config = app(ConfigService::class);
|
|
$expectedClientId = $config->nodeClientId();
|
|
$secret = $config->nodeSharedSecret();
|
|
|
|
if ($expectedClientId === '' || $secret === '') {
|
|
abort(500, 'Node API credentials not configured (settings: node_client_id, node_shared_secret)');
|
|
}
|
|
|
|
if ($clientId !== $expectedClientId) {
|
|
abort(401, 'Invalid client id');
|
|
}
|
|
|
|
|
|
|
|
$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);
|
|
}
|
|
}
|