‪TYPO3CMS  ‪main
AbstractContentSecurityPolicyReporter.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Http\Message\ServerRequestInterface;
21 use Psr\Http\Server\MiddlewareInterface;
31 
35 abstract class ‪AbstractContentSecurityPolicyReporter implements MiddlewareInterface
36 {
37  protected const ‪URI_KEYS = ['document-uri', 'report-uri', 'blocked-uri', 'referrer'];
38 
39  public function ‪__construct(
40  protected readonly ‪PolicyProvider $policyProvider,
41  protected readonly ‪ReportRepository $reportRepository,
42  protected readonly ‪HashService $hashService,
43  ) {}
44 
45  protected function ‪persistCspReport(‪Scope $scope, ServerRequestInterface $request): void
46  {
47  $payload = (string)$request->getBody();
48  if (!$this->‪isJson($payload)) {
49  return;
50  }
51  $normalizedParams = $request->getAttribute('normalizedParams');
52  $meta = [
53  'addr' => ‪IpAnonymizationUtility::anonymizeIp($normalizedParams->getRemoteAddress()),
54  'agent' => $normalizedParams->getHttpUserAgent(),
55  ];
56  $requestTime = (int)($request->getQueryParams()['requestTime'] ?? 0);
57  $originalDetails = json_decode($payload, true)['csp-report'] ?? [];
58  $originalDetails = $this->‪anonymizeDetails($originalDetails);
59  ‪$details = new ‪ReportDetails($originalDetails);
60  $summary = $this->‪generateReportSummary($scope, ‪$details);
61  $report = new ‪Report(
62  $scope,
63  ReportStatus::New,
64  $requestTime,
65  $meta,
67  $summary
68  );
69  $this->reportRepository->add($report);
70  }
71 
72  protected function ‪generateReportSummary(‪Scope $scope, ‪ReportDetails ‪$details): string
73  {
74  return $this->hashService->hmac(
75  json_encode([
76  $scope,
77  ‪$details['effective-directive'],
78  ‪$details['blocked-uri'],
79  ‪$details['script-sample'] ?? null,
80  ]),
81  self::class,
82  );
83  }
84 
85  protected function ‪anonymizeDetails(array ‪$details): array
86  {
87  foreach (self::URI_KEYS as $uriKey) {
88  if (!isset(‪$details[$uriKey])) {
89  continue;
90  }
91  ‪$details[$uriKey] = $this->‪anonymizeUri($details[$uriKey]);
92  }
93  return ‪$details;
94  }
95 
96  protected function ‪anonymizeUri(string $value): string
97  {
98  $uri = new ‪Uri($value);
99  if ($uri->getQuery() === '') {
100  return $value;
101  }
102  parse_str($uri->getQuery(), $query);
103  // strip CSRF token (might be a different usage as well)
104  unset($query['token']);
105  return (string)$uri->withQuery(http_build_query($query, '', '&', PHP_QUERY_RFC3986));
106  }
107 
108  protected function ‪isCspReport(‪Scope $scope, ServerRequestInterface $request): bool
109  {
110  $normalizedParams = $request->getAttribute('normalizedParams');
111  $contentTypeHeader = $request->getHeaderLine('content-type');
112 
113  // @todo
114  // + verify current session
115  // + invoke rate limiter
116  // + check additional scope (snippet enrichment)
117 
118  $reportingUriBase = $this->policyProvider->getDefaultReportingUriBase($scope, $request, false);
119  return $request->getMethod() === 'POST'
120  && str_starts_with($normalizedParams->getRequestUri(), (string)$reportingUriBase)
121  && $contentTypeHeader === 'application/csp-report';
122  }
123 
124  protected function ‪isJson(string $value): bool
125  {
126  try {
127  json_decode($value, false, 16, JSON_THROW_ON_ERROR);
128  return true;
129  } catch (\JsonException) {
130  return false;
131  }
132  }
133 }
‪TYPO3\CMS\Webhooks\Message\$details
‪identifier readonly UriInterface readonly array $details
Definition: MfaVerificationErrorOccurredMessage.php:37
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\ReportRepository
Definition: ReportRepository.php:31
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\anonymizeDetails
‪anonymizeDetails(array $details)
Definition: AbstractContentSecurityPolicyReporter.php:85
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\__construct
‪__construct(protected readonly PolicyProvider $policyProvider, protected readonly ReportRepository $reportRepository, protected readonly HashService $hashService,)
Definition: AbstractContentSecurityPolicyReporter.php:39
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\isCspReport
‪isCspReport(Scope $scope, ServerRequestInterface $request)
Definition: AbstractContentSecurityPolicyReporter.php:108
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\persistCspReport
‪persistCspReport(Scope $scope, ServerRequestInterface $request)
Definition: AbstractContentSecurityPolicyReporter.php:45
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\URI_KEYS
‪const URI_KEYS
Definition: AbstractContentSecurityPolicyReporter.php:37
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\anonymizeUri
‪anonymizeUri(string $value)
Definition: AbstractContentSecurityPolicyReporter.php:96
‪TYPO3\CMS\Core\Http\Uri
Definition: Uri.php:30
‪TYPO3\CMS\Core\Utility\IpAnonymizationUtility
Definition: IpAnonymizationUtility.php:26
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\ReportStatus
‪ReportStatus
Definition: ReportStatus.php:24
‪TYPO3\CMS\Core\Utility\IpAnonymizationUtility\anonymizeIp
‪static anonymizeIp(string $address, int $mask=null)
Definition: IpAnonymizationUtility.php:60
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Scope
Definition: Scope.php:30
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\ReportDetails
Definition: ReportDetails.php:24
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter
Definition: AbstractContentSecurityPolicyReporter.php:36
‪TYPO3\CMS\Core\Middleware
Definition: AbstractContentSecurityPolicyReporter.php:18
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\Report
Definition: Report.php:27
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\generateReportSummary
‪generateReportSummary(Scope $scope, ReportDetails $details)
Definition: AbstractContentSecurityPolicyReporter.php:72
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\isJson
‪isJson(string $value)
Definition: AbstractContentSecurityPolicyReporter.php:124
‪TYPO3\CMS\Core\Crypto\HashService
Definition: HashService.php:27
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\PolicyProvider
Definition: PolicyProvider.php:38