3 namespace Friendica\App;
5 use Friendica\Core\Config\Capability\IManageConfigValues;
8 * Container for the whole request
10 * @see https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface
12 * @todo future container class for whole requests, currently it's not :-)
16 /** @var string the default possible headers, which could contain the client IP */
17 const ORDERED_FORWARD_FOR_HEADER = 'HTTP_X_FORWARDED_FOR';
19 /** @var string The remote IP address of the current request */
20 protected $remoteAddress;
23 * @return string The remote IP address of the current request
25 public function getRemoteAddress(): string
27 return $this->remoteAddress;
30 public function __construct(IManageConfigValues $config, array $server = [])
32 $this->remoteAddress = $this->determineRemoteAddress($config, $server);
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
42 * @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
44 protected function matchesTrustedProxy(string $trustedProxy, string $remoteAddress): bool
46 $cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
48 if (preg_match($cidrre, $trustedProxy, $match)) {
50 $shiftbits = min(32, max(0, 32 - intval($match[2])));
51 $netnum = ip2long($net) >> $shiftbits;
52 $ipnum = ip2long($remoteAddress) >> $shiftbits;
54 return $ipnum === $netnum;
57 return $trustedProxy === $remoteAddress;
61 * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
62 * For details regarding what "match" means, refer to `matchesTrustedProxy`.
64 * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
66 protected function isTrustedProxy(array $trustedProxies, string $remoteAddress): bool
68 foreach ($trustedProxies as $tp) {
69 if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
78 * @param IManageConfigValues $config
79 * @param array $server
83 protected function determineRemoteAddress(IManageConfigValues $config, array $server): string
85 $remoteAddress = $server['REMOTE_ADDR'] ?? '0.0.0.0';
86 $trustedProxies = preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', $config->get('proxy', 'trusted_proxies', ''));
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;
91 foreach ($forwardedForHeaders as $header) {
92 if (isset($server[$header])) {
93 foreach (explode(',', $server[$header]) as $IP) {
96 // remove brackets from IPv6 addresses
97 if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
98 $IP = substr($IP, 1, -1);
101 // skip trusted proxies in the list itself
102 if ($this->isTrustedProxy($trustedProxies, $IP)) {
106 if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
114 return $remoteAddress;