]> git.mxchange.org Git - friendica.git/blob - src/App/Request.php
7eb31b22d7c4456f4cd3452d4edbc607544ecfdf
[friendica.git] / src / App / Request.php
1 <?php
2
3 namespace Friendica\App;
4
5 use Friendica\Core\Config\Capability\IManageConfigValues;
6
7 /**
8  * Container for the whole request
9  *
10  * @see https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface
11  *
12  * @todo future container class for whole requests, currently it's not :-)
13  */
14 class Request
15 {
16         /** @var string the default possible headers, which could contain the client IP */
17         const ORDERED_FORWARD_FOR_HEADER = 'HTTP_X_FORWARDED_FOR';
18
19         /** @var string The remote IP address of the current request */
20         protected $remoteAddress;
21
22         /**
23          * @return string The remote IP address of the current request
24          */
25         public function getRemoteAddress(): string
26         {
27                 return $this->remoteAddress;
28         }
29
30         public function __construct(IManageConfigValues $config, array $server = [])
31         {
32                 $this->remoteAddress = $this->determineRemoteAddress($config, $server);
33         }
34
35         /**
36          * Checks if given $remoteAddress matches given $trustedProxy.
37          * If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
38          * $remoteAddress is an IPv4 address within that IP range.
39          * Otherwise, $remoteAddress will be compared to $trustedProxy literally and the result
40          * will be returned.
41          *
42          * @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
43          */
44         protected function matchesTrustedProxy(string $trustedProxy, string $remoteAddress): bool
45         {
46                 $cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
47
48                 if (preg_match($cidrre, $trustedProxy, $match)) {
49                         $net       = $match[1];
50                         $shiftbits = min(32, max(0, 32 - intval($match[2])));
51                         $netnum    = ip2long($net) >> $shiftbits;
52                         $ipnum     = ip2long($remoteAddress) >> $shiftbits;
53
54                         return $ipnum === $netnum;
55                 }
56
57                 return $trustedProxy === $remoteAddress;
58         }
59
60         /**
61          * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
62          * For details regarding what "match" means, refer to `matchesTrustedProxy`.
63          *
64          * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
65          */
66         protected function isTrustedProxy(array $trustedProxies, string $remoteAddress): bool
67         {
68                 foreach ($trustedProxies as $tp) {
69                         if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
70                                 return true;
71                         }
72                 }
73
74                 return false;
75         }
76
77         /**
78          * @param IManageConfigValues $config
79          * @param array               $server
80          *
81          * @return string
82          */
83         protected function determineRemoteAddress(IManageConfigValues $config, array $server): string
84         {
85                 $remoteAddress  = $server['REMOTE_ADDR'] ?? '0.0.0.0';
86                 $trustedProxies = preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', $config->get('proxy', 'trusted_proxies', ''));
87
88                 if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
89                         $forwardedForHeaders = preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', $config->get('proxy', 'forwarded_for_headers')) ?? static::ORDERED_FORWARD_FOR_HEADER;
90
91                         foreach ($forwardedForHeaders as $header) {
92                                 if (isset($server[$header])) {
93                                         foreach (explode(',', $server[$header]) as $IP) {
94                                                 $IP = trim($IP);
95
96                                                 // remove brackets from IPv6 addresses
97                                                 if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
98                                                         $IP = substr($IP, 1, -1);
99                                                 }
100
101                                                 // skip trusted proxies in the list itself
102                                                 if ($this->isTrustedProxy($trustedProxies, $IP)) {
103                                                         continue;
104                                                 }
105
106                                                 if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
107                                                         return $IP;
108                                                 }
109                                         }
110                                 }
111                         }
112                 }
113
114                 return $remoteAddress;
115         }
116 }