‪TYPO3CMS  11.5
ReferrerEnforcer.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\ResponseInterface;
21 use Psr\Http\Message\ServerRequestInterface;
26 
31 {
32  private const ‪TYPE_REFERRER_EMPTY = 1;
33  private const ‪TYPE_REFERRER_SAME_SITE = 2;
34  private const ‪TYPE_REFERRER_SAME_ORIGIN = 4;
35 
39  protected ‪$request;
40 
44  protected ‪$requestHost;
45 
49  protected ‪$requestDir;
50 
51  public function ‪__construct(ServerRequestInterface ‪$request)
52  {
53  $this->request = ‪$request;
54  $this->requestHost = rtrim($this->‪resolveRequestHost($request), '/') . '/';
55  $this->requestDir = $this->‪resolveRequestDir($request);
56  }
57 
58  public function ‪handle(array $options = null): ?ResponseInterface
59  {
60  $referrerType = $this->‪resolveReferrerType();
61  // valid referrer, no more actions required
62  if ($referrerType & self::TYPE_REFERRER_SAME_ORIGIN) {
63  return null;
64  }
65  $flags = $options['flags'] ?? [];
66  $expiration = $options['expiration'] ?? 5;
67  // referrer is missing and route requested to refresh
68  // (created HTML refresh to enforce having referrer)
69  if (($this->request->getQueryParams()['referrer-refresh'] ?? 0) <= time()
70  && (
71  in_array('refresh-always', $flags, true)
72  || ($referrerType & self::TYPE_REFERRER_EMPTY && in_array('refresh-empty', $flags, true))
73  || ($referrerType & self::TYPE_REFERRER_SAME_SITE && in_array('refresh-same-site', $flags, true))
74  )
75  ) {
76  $refreshParameter = 'referrer-refresh=' . (time() + $expiration);
77  $refreshUri = $this->request->getUri();
78  $query = $refreshUri->getQuery();
79  $refreshUri = $refreshUri->withQuery(
80  $query !== '' ? $query . '&' . $refreshParameter : $refreshParameter
81  );
82  $scriptUri = $this->‪resolveAbsoluteWebPath(
83  'EXT:core/Resources/Public/JavaScript/ReferrerRefresh.js'
84  );
85  // simulating navigate event by clicking anchor link
86  // since meta-refresh won't change `document.referrer` in e.g. Firefox
87  return new ‪HtmlResponse(sprintf(
88  '<html>'
89  . '<head><link rel="icon" href="data:image/svg+xml,"></head>'
90  . '<body><a href="%1$s" id="referrer-refresh">&nbsp;</a>'
91  . '<script src="%2$s"></script></body>'
92  . '</html>',
93  htmlspecialchars((string)$refreshUri),
94  htmlspecialchars($scriptUri)
95  ));
96  }
97  $subject = $options['subject'] ?? '';
98  if ($referrerType & self::TYPE_REFERRER_EMPTY) {
99  // still empty referrer or invalid referrer, deny route invocation
100  throw new MissingReferrerException(
101  sprintf('Missing referrer%s', $subject !== '' ? ' for ' . $subject : ''),
102  1588095935
103  );
104  }
105  // referrer is given, but does not match current base URL
106  throw new InvalidReferrerException(
107  sprintf('Invalid referrer%s', $subject !== '' ? ' for ' . $subject : ''),
108  1588095936
109  );
110  }
111 
112  protected function ‪resolveAbsoluteWebPath(string $target): string
113  {
115  }
116 
117  protected function ‪resolveReferrerType(): int
118  {
119  $referrer = $this->request->getServerParams()['HTTP_REFERER'] ?? '';
120  if ($referrer === '') {
122  }
123  if (strpos($referrer, $this->requestDir) === 0) {
124  // same-origin implies same-site
125  return self::TYPE_REFERRER_SAME_ORIGIN | ‪self::TYPE_REFERRER_SAME_SITE;
126  }
127  if (strpos($referrer, $this->requestHost) === 0) {
129  }
130  return 0;
131  }
132 
133  protected function ‪resolveRequestHost(ServerRequestInterface ‪$request): string
134  {
135  $normalizedParams = ‪$request->getAttribute('normalizedParams');
136  if ($normalizedParams instanceof ‪NormalizedParams) {
137  return $normalizedParams->getRequestHost();
138  }
139  return GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST');
140  }
141 
142  protected function ‪resolveRequestDir(ServerRequestInterface ‪$request): string
143  {
144  $normalizedParams = ‪$request->getAttribute('normalizedParams');
145  if ($normalizedParams instanceof ‪NormalizedParams) {
146  return $normalizedParams->getRequestDir();
147  }
148  return GeneralUtility::getIndpEnv('TYPO3_REQUEST_DIR');
149  }
150 }
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\resolveReferrerType
‪resolveReferrerType()
Definition: ReferrerEnforcer.php:114
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer
Definition: ReferrerEnforcer.php:31
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:25
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\__construct
‪__construct(ServerRequestInterface $request)
Definition: ReferrerEnforcer.php:48
‪TYPO3\CMS\Core\Utility\PathUtility\getPublicResourceWebPath
‪static string getPublicResourceWebPath(string $resourcePath, bool $prefixWithSitePath=true)
Definition: PathUtility.php:98
‪TYPO3\CMS\Core\Http\Security
Definition: InvalidReferrerException.php:16
‪TYPO3\CMS\Core\Http\Security\MissingReferrerException
Definition: MissingReferrerException.php:23
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\$request
‪ServerRequestInterface $request
Definition: ReferrerEnforcer.php:38
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\resolveRequestDir
‪resolveRequestDir(ServerRequestInterface $request)
Definition: ReferrerEnforcer.php:139
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\TYPE_REFERRER_SAME_SITE
‪const TYPE_REFERRER_SAME_SITE
Definition: ReferrerEnforcer.php:33
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\handle
‪handle(array $options=null)
Definition: ReferrerEnforcer.php:55
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\$requestDir
‪string $requestDir
Definition: ReferrerEnforcer.php:46
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\TYPE_REFERRER_EMPTY
‪const TYPE_REFERRER_EMPTY
Definition: ReferrerEnforcer.php:32
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\resolveRequestHost
‪resolveRequestHost(ServerRequestInterface $request)
Definition: ReferrerEnforcer.php:130
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Http\Security\InvalidReferrerException
Definition: InvalidReferrerException.php:23
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\TYPE_REFERRER_SAME_ORIGIN
‪const TYPE_REFERRER_SAME_ORIGIN
Definition: ReferrerEnforcer.php:34
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\$requestHost
‪string $requestHost
Definition: ReferrerEnforcer.php:42
‪TYPO3\CMS\Core\Http\NormalizedParams
Definition: NormalizedParams.php:35
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\resolveAbsoluteWebPath
‪resolveAbsoluteWebPath(string $target)
Definition: ReferrerEnforcer.php:109
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:26