Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
CsrfAuditMiddleware
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
2 / 2
10
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 process
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
9
1<?php
2
3declare(strict_types=1);
4
5namespace App\Shared\Security;
6
7use App\Shared\Logging\CentralLogger;
8use Psr\Http\Message\ResponseInterface;
9use Psr\Http\Message\ServerRequestInterface;
10use Psr\Http\Server\MiddlewareInterface;
11use Psr\Http\Server\RequestHandlerInterface;
12use Yiisoft\Csrf\CsrfTokenInterface;
13
14 class CsrfAuditMiddleware implements MiddlewareInterface
15{
16    public function __construct(private  CsrfTokenInterface $csrfToken) {}
17
18    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
19    {
20        $path = $request->getUri()->getPath();
21        if (str_starts_with($path, '/api/v1/') || $request->hasHeader('X-Api-Signature')) {
22            return $handler->handle($request);
23        }
24
25        $response = $handler->handle($request);
26
27        if (in_array($response->getStatusCode(), [422, 403], true) && in_array($request->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'], true)) {
28            /** @var array<string, mixed> $parsedBody */
29            $parsedBody = (array) ($request->getParsedBody() ?? []);
30            $csrfInBody = isset($parsedBody['_csrf']) && is_string($parsedBody['_csrf']) ? $parsedBody['_csrf'] : 'NONE';
31            $providedToken = $request->getHeaderLine('X-CSRF-Token') ?: $csrfInBody;
32
33            CentralLogger::logSecurity('CSRF_TOKEN_MISMATCH', [
34                'from_url' => $request->getHeaderLine('Referer') ?: 'DIRECT/UNKNOWN',
35                'to_url' => (string) $request->getUri(),
36                'provided_token' => $providedToken,
37                'current_session_token' => $this->csrfToken->getValue(),
38            ]);
39        }
40
41        return $response;
42    }
43}