‪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  ) {}
43 
44  protected function ‪persistCspReport(‪Scope $scope, ServerRequestInterface $request): void
45  {
46  $payload = (string)$request->getBody();
47  if (!$this->‪isJson($payload)) {
48  return;
49  }
50  $normalizedParams = $request->getAttribute('normalizedParams');
51  $meta = [
52  'addr' => ‪IpAnonymizationUtility::anonymizeIp($normalizedParams->getRemoteAddress()),
53  'agent' => $normalizedParams->getHttpUserAgent(),
54  ];
55  $requestTime = (int)($request->getQueryParams()['requestTime'] ?? 0);
56  $originalDetails = json_decode($payload, true)['csp-report'] ?? [];
57  $originalDetails = $this->‪anonymizeDetails($originalDetails);
58  ‪$details = new ‪ReportDetails($originalDetails);
59  $summary = $this->‪generateReportSummary($scope, ‪$details);
60  $report = new ‪Report(
61  $scope,
62  ReportStatus::New,
63  $requestTime,
64  $meta,
66  $summary
67  );
68  $this->reportRepository->add($report);
69  }
70 
71  protected function ‪generateReportSummary(‪Scope $scope, ‪ReportDetails ‪$details): string
72  {
74  json_encode([
75  $scope,
76  ‪$details['effective-directive'],
77  ‪$details['blocked-uri'],
78  ‪$details['script-sample'] ?? null,
79  ]),
80  self::class,
81  );
82  }
83 
84  protected function ‪anonymizeDetails(array ‪$details): array
85  {
86  foreach (self::URI_KEYS as $uriKey) {
87  if (!isset(‪$details[$uriKey])) {
88  continue;
89  }
90  ‪$details[$uriKey] = $this->‪anonymizeUri($details[$uriKey]);
91  }
92  return ‪$details;
93  }
94 
95  protected function ‪anonymizeUri(string $value): string
96  {
97  $uri = new ‪Uri($value);
98  if ($uri->getQuery() === '') {
99  return $value;
100  }
101  parse_str($uri->getQuery(), $query);
102  // strip CSRF token (might be a different usage as well)
103  unset($query['token']);
104  return (string)$uri->withQuery(http_build_query($query, '', '&', PHP_QUERY_RFC3986));
105  }
106 
107  protected function ‪isCspReport(‪Scope $scope, ServerRequestInterface $request): bool
108  {
109  $normalizedParams = $request->getAttribute('normalizedParams');
110  $contentTypeHeader = $request->getHeaderLine('content-type');
111 
112  // @todo
113  // + verify current session
114  // + invoke rate limiter
115  // + check additional scope (snippet enrichment)
116 
117  $reportingUriBase = $this->policyProvider->getDefaultReportingUriBase($scope, $request, false);
118  return $request->getMethod() === 'POST'
119  && str_starts_with($normalizedParams->getRequestUri(), (string)$reportingUriBase)
120  && $contentTypeHeader === 'application/csp-report';
121  }
122 
123  protected function ‪isJson(string $value): bool
124  {
125  try {
126  json_decode($value, false, 16, JSON_THROW_ON_ERROR);
127  return true;
128  } catch (\JsonException) {
129  return false;
130  }
131  }
132 }
‪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:84
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\isCspReport
‪isCspReport(Scope $scope, ServerRequestInterface $request)
Definition: AbstractContentSecurityPolicyReporter.php:107
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\persistCspReport
‪persistCspReport(Scope $scope, ServerRequestInterface $request)
Definition: AbstractContentSecurityPolicyReporter.php:44
‪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:95
‪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\Utility\GeneralUtility\hmac
‪static string hmac($input, $additionalSecret='')
Definition: GeneralUtility.php:473
‪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:71
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\isJson
‪isJson(string $value)
Definition: AbstractContentSecurityPolicyReporter.php:123
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\PolicyProvider
Definition: PolicyProvider.php:37
‪TYPO3\CMS\Core\Middleware\AbstractContentSecurityPolicyReporter\__construct
‪__construct(protected readonly PolicyProvider $policyProvider, protected readonly ReportRepository $reportRepository)
Definition: AbstractContentSecurityPolicyReporter.php:39