|
11 | 11 |
|
12 | 12 | namespace Symfony\Component\HttpFoundation;
|
13 | 13 |
|
| 14 | +use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException; |
14 | 15 | use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
15 | 16 |
|
16 | 17 | /**
|
@@ -805,41 +806,34 @@ public function getClientIps()
|
805 | 806 | return array($ip);
|
806 | 807 | }
|
807 | 808 |
|
808 |
| - if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { |
| 809 | + $hasTrustedForwardedHeader = self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED]); |
| 810 | + $hasTrustedClientIpHeader = self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP]); |
| 811 | + |
| 812 | + if ($hasTrustedForwardedHeader) { |
809 | 813 | $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
|
810 | 814 | preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
|
811 |
| - $clientIps = $matches[3]; |
812 |
| - } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { |
813 |
| - $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); |
814 |
| - } |
| 815 | + $forwardedClientIps = $matches[3]; |
815 | 816 |
|
816 |
| - $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from |
817 |
| - $firstTrustedIp = null; |
818 |
| - |
819 |
| - foreach ($clientIps as $key => $clientIp) { |
820 |
| - // Remove port (unfortunately, it does happen) |
821 |
| - if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { |
822 |
| - $clientIps[$key] = $clientIp = $match[1]; |
823 |
| - } |
| 817 | + $forwardedClientIps = $this->normalizeAndFilterClientIps($forwardedClientIps, $ip); |
| 818 | + $clientIps = $forwardedClientIps; |
| 819 | + } |
824 | 820 |
|
825 |
| - if (!filter_var($clientIp, FILTER_VALIDATE_IP)) { |
826 |
| - unset($clientIps[$key]); |
| 821 | + if ($hasTrustedClientIpHeader) { |
| 822 | + $xForwardedForClientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); |
827 | 823 |
|
828 |
| - continue; |
829 |
| - } |
| 824 | + $xForwardedForClientIps = $this->normalizeAndFilterClientIps($xForwardedForClientIps, $ip); |
| 825 | + $clientIps = $xForwardedForClientIps; |
| 826 | + } |
830 | 827 |
|
831 |
| - if (IpUtils::checkIp($clientIp, self::$trustedProxies)) { |
832 |
| - unset($clientIps[$key]); |
| 828 | + if ($hasTrustedForwardedHeader && $hasTrustedClientIpHeader && $forwardedClientIps !== $xForwardedForClientIps) { |
| 829 | + throw new ConflictingHeadersException('The request has both a trusted Forwarded header and a trusted Client IP header, conflicting with each other with regards to the originating IP addresses of the request. This is the result of a misconfiguration. You should either configure your proxy only to send one of these headers, or configure Symfony to distrust one of them.'); |
| 830 | + } |
833 | 831 |
|
834 |
| - // Fallback to this when the client IP falls into the range of trusted proxies |
835 |
| - if (null === $firstTrustedIp) { |
836 |
| - $firstTrustedIp = $clientIp; |
837 |
| - } |
838 |
| - } |
| 832 | + if (!$hasTrustedForwardedHeader && !$hasTrustedClientIpHeader) { |
| 833 | + return $this->normalizeAndFilterClientIps(array(), $ip); |
839 | 834 | }
|
840 | 835 |
|
841 |
| - // Now the IP chain contains only untrusted proxies and the client IP |
842 |
| - return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp); |
| 836 | + return $clientIps; |
843 | 837 | }
|
844 | 838 |
|
845 | 839 | /**
|
@@ -1930,4 +1924,35 @@ private function isFromTrustedProxy()
|
1930 | 1924 | {
|
1931 | 1925 | return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
|
1932 | 1926 | }
|
| 1927 | + |
| 1928 | + private function normalizeAndFilterClientIps(array $clientIps, $ip) |
| 1929 | + { |
| 1930 | + $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from |
| 1931 | + $firstTrustedIp = null; |
| 1932 | + |
| 1933 | + foreach ($clientIps as $key => $clientIp) { |
| 1934 | + // Remove port (unfortunately, it does happen) |
| 1935 | + if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { |
| 1936 | + $clientIps[$key] = $clientIp = $match[1]; |
| 1937 | + } |
| 1938 | + |
| 1939 | + if (!filter_var($clientIp, FILTER_VALIDATE_IP)) { |
| 1940 | + unset($clientIps[$key]); |
| 1941 | + |
| 1942 | + continue; |
| 1943 | + } |
| 1944 | + |
| 1945 | + if (IpUtils::checkIp($clientIp, self::$trustedProxies)) { |
| 1946 | + unset($clientIps[$key]); |
| 1947 | + |
| 1948 | + // Fallback to this when the client IP falls into the range of trusted proxies |
| 1949 | + if (null === $firstTrustedIp) { |
| 1950 | + $firstTrustedIp = $clientIp; |
| 1951 | + } |
| 1952 | + } |
| 1953 | + } |
| 1954 | + |
| 1955 | + // Now the IP chain contains only untrusted proxies and the client IP |
| 1956 | + return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp); |
| 1957 | + } |
1933 | 1958 | }
|
0 commit comments