SignatureCheckListener With Symfony’s AsEventListener Attribute
# .env file APP_CHECK_SIGNATURE=true<?php namespace App\Listener; use App\Enum\Headers; use App\Exception\UnmatchedSignatureException; use App\Service\SignatureService; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; #[AsEventListener] class SignatureCheckListener { public function __construct( #[Autowire(env: 'bool:APP_CHECK_SIGNATURE')] private readonly bool $checkSignature, private readonly TokenStorageInterface $tokenStorage, private readonly SignatureService $signatureService, private readonly LoggerInterface $logger, ) { } /** * @param RequestEvent $event * @return void * @throws UnmatchedSignatureException */ public function __invoke(RequestEvent $event): void { if (!$this->checkSignature) { return; } $request = $event->getRequest(); $signature = $request->headers->get(Headers::SIGNATURE->value) ?? ''; $user = $this->tokenStorage->getToken()?->getUser(); if (!$this->signatureService->check($user, $request, $signature)) { $this->logger->warning(sprintf('Unmatched Signature: %s', $signature)); throw new UnmatchedSignatureException(); } } }The AsEventListener attribute accepts the following parameters./symfony/event-dispatcher/Attribute/AsEventListener.php /** * @param string|null $event The event name to listen to * @param string|null $method The method to run when the listened event is triggered * @param int $priority The priority of this listener if several are declared for the same event * @param string|null $dispatcher The service id of the event dispatcher to listen to */Storing the header parameters in a PHP Enum can be very useful for easier management.<?php namespace App\Enum; enum Headers: string { case SIGNATURE = 'X-Signature'; }We’re creating a service to calculate the signature.<?php namespace App\Service; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\User\UserInterface; class SignatureService { public function __construct() { } public function check(?UserInterface $user, Request $request, string $signature): bool { if (hash_equals($signature, $this->calculateSignature($user, $request))) { return true; } return false; } private function calculateSignature(?UserInterface $user, Request $request): string { //your algorithm, this is example return 'your_calculated_signature'; } }Essentially, we aim to re-authenticate a request encrypted with a flag known to both the user and the server, thereby protecting the request from MITM (Man-in-the-middle) attacks. You can explore the Diffie-Hellman algorithm to securely store a private key on the user side without transmitting it over the network.
1 месяц назад