]> git.mxchange.org Git - friendica.git/blob - src/App/Request.php
API: Accept "redirect_uris" as both array and string
[friendica.git] / src / App / Request.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2023, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\App;
23
24 use Friendica\Core\Config\Capability\IManageConfigValues;
25 use Friendica\Core\System;
26
27 /**
28  * Container for the whole request
29  *
30  * @see https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface
31  *
32  * @todo future container class for whole requests, currently it's not :-)
33  */
34 class Request
35 {
36         /**
37          * A comma separated list of default headers that could contain the client IP in a proxy request
38          *
39          * @var string
40          */
41         const DEFAULT_FORWARD_FOR_HEADER = 'HTTP_X_FORWARDED_FOR';
42         /**
43          * The default Request-ID header to retrieve the current transaction ID from the HTTP header (if set)
44          *
45          * @var string
46          */
47         const DEFAULT_REQUEST_ID_HEADER = 'HTTP_X_REQUEST_ID';
48
49         /** @var string The remote IP address of the current request */
50         protected $remoteAddress;
51         /** @var string The request-id of the current request */
52         protected $requestId;
53
54         /**
55          * @return string The remote IP address of the current request
56          *
57          * Do always use this instead of $_SERVER['REMOTE_ADDR']
58          */
59         public function getRemoteAddress(): string
60         {
61                 return $this->remoteAddress;
62         }
63
64         /**
65          * @return string The request ID of the current request
66          *
67          * Do always use this instead of $_SERVER['X_REQUEST_ID']
68          */
69         public function getRequestId(): string
70         {
71                 return $this->requestId;
72         }
73
74         public function __construct(IManageConfigValues $config, array $server = [])
75         {
76                 $this->remoteAddress = $this->determineRemoteAddress($config, $server);
77                 $this->requestId = $server[static::DEFAULT_REQUEST_ID_HEADER] ?? System::createGUID(8);
78         }
79
80         /**
81          * Checks if given $remoteAddress matches given $trustedProxy.
82          * If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
83          * $remoteAddress is an IPv4 address within that IP range.
84          * Otherwise, $remoteAddress will be compared to $trustedProxy literally and the result
85          * will be returned.
86          *
87          * @param string $trustedProxy  The current, trusted proxy to check
88          * @param string $remoteAddress The current remote IP address
89          *
90          *
91          * @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
92          */
93         protected function matchesTrustedProxy(string $trustedProxy, string $remoteAddress): bool
94         {
95                 $cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
96
97                 if (preg_match($cidrre, $trustedProxy, $match)) {
98                         $net       = $match[1];
99                         $shiftbits = min(32, max(0, 32 - intval($match[2])));
100                         $netnum    = ip2long($net) >> $shiftbits;
101                         $ipnum     = ip2long($remoteAddress) >> $shiftbits;
102
103                         return $ipnum === $netnum;
104                 }
105
106                 return $trustedProxy === $remoteAddress;
107         }
108
109         /**
110          * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
111          * For details regarding what "match" means, refer to `matchesTrustedProxy`.
112          *
113          * @param string[] $trustedProxies A list of the trusted proxies
114          * @param string   $remoteAddress  The current remote IP address
115          *
116          * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
117          */
118         protected function isTrustedProxy(array $trustedProxies, string $remoteAddress): bool
119         {
120                 foreach ($trustedProxies as $tp) {
121                         if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
122                                 return true;
123                         }
124                 }
125
126                 return false;
127         }
128
129         /**
130          * Determines the remote address, if the connection came from a trusted proxy
131          * and `forwarded_for_headers` has been configured then the IP address
132          * specified in this header will be returned instead.
133          *
134          * @param IManageConfigValues $config
135          * @param array               $server The $_SERVER array
136          *
137          * @return string
138          */
139         protected function determineRemoteAddress(IManageConfigValues $config, array $server): string
140         {
141                 $remoteAddress  = $server['REMOTE_ADDR'] ?? '0.0.0.0';
142                 $trustedProxies = preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', $config->get('proxy', 'trusted_proxies', ''));
143
144                 if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
145                         $forwardedForHeaders = preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', $config->get('proxy', 'forwarded_for_headers', static::DEFAULT_FORWARD_FOR_HEADER));
146
147                         foreach ($forwardedForHeaders as $header) {
148                                 if (isset($server[$header])) {
149                                         foreach (explode(',', $server[$header]) as $IP) {
150                                                 $IP = trim($IP);
151
152                                                 // remove brackets from IPv6 addresses
153                                                 if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
154                                                         $IP = substr($IP, 1, -1);
155                                                 }
156
157                                                 // skip trusted proxies in the list itself
158                                                 if ($this->isTrustedProxy($trustedProxies, $IP)) {
159                                                         continue;
160                                                 }
161
162                                                 if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
163                                                         return $IP;
164                                                 }
165                                         }
166                                 }
167                         }
168                 }
169
170                 return $remoteAddress;
171         }
172 }