‪TYPO3CMS  10.4
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  $refreshUri = $this->request->getUri();
77  $refreshUri = $refreshUri->withQuery(
78  $refreshUri->getQuery() . '&referrer-refresh=' . (time() + $expiration)
79  );
80  $scriptUri = $this->‪resolveAbsoluteWebPath(
81  'EXT:core/Resources/Public/JavaScript/ReferrerRefresh.js'
82  );
83  // simulating navigate event by clicking anchor link
84  // since meta-refresh won't change `document.referrer` in e.g. Firefox
85  return new ‪HtmlResponse(sprintf(
86  '<html>'
87  . '<head><link rel="icon" href="data:image/svg+xml,"></head>'
88  . '<body><a href="%1$s" id="referrer-refresh">&nbsp;</a>'
89  . '<script src="%2$s"></script></body>'
90  . '</html>',
91  htmlspecialchars((string)$refreshUri),
92  htmlspecialchars($scriptUri)
93  ));
94  }
95  $subject = $options['subject'] ?? '';
96  if ($referrerType & self::TYPE_REFERRER_EMPTY) {
97  // still empty referrer or invalid referrer, deny route invocation
98  throw new MissingReferrerException(
99  sprintf('Missing referrer%s', $subject !== '' ? ' for ' . $subject : ''),
100  1588095935
101  );
102  }
103  // referrer is given, but does not match current base URL
104  throw new InvalidReferrerException(
105  sprintf('Invalid referrer%s', $subject !== '' ? ' for ' . $subject : ''),
106  1588095936
107  );
108  }
109 
110  protected function ‪resolveAbsoluteWebPath(string $target): string
111  {
113  GeneralUtility::getFileAbsFileName($target)
114  );
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:24
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer\__construct
‪__construct(ServerRequestInterface $request)
Definition: ReferrerEnforcer.php:48
‪TYPO3\CMS\Core\Http\Security
Definition: InvalidReferrerException.php:16
‪TYPO3\CMS\Core\Http\Security\MissingReferrerException
Definition: MissingReferrerException.php:24
‪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:46
‪TYPO3\CMS\Core\Utility\PathUtility\getAbsoluteWebPath
‪static string getAbsoluteWebPath($targetPath)
Definition: PathUtility.php:43
‪TYPO3\CMS\Core\Http\Security\InvalidReferrerException
Definition: InvalidReferrerException.php:24
‪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:107
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:26