|
30 | 30 | */
|
31 | 31 | class Request
|
32 | 32 | {
|
33 |
| - protected static $trustProxy = false; |
| 33 | + const HEADER_CLIENT_IP = 'client_ip'; |
| 34 | + const HEADER_CLIENT_HOST = 'client_host'; |
| 35 | + const HEADER_CLIENT_PROTO = 'client_proto'; |
| 36 | + const HEADER_CLIENT_PORT = 'client_port'; |
| 37 | + |
| 38 | + protected static $trustProxyData = false; |
| 39 | + |
| 40 | + protected static $trustedProxies = array(); |
| 41 | + |
| 42 | + /** |
| 43 | + * Names for headers that can be trusted when |
| 44 | + * using trusted proxies. |
| 45 | + * |
| 46 | + * The default names are non-standard, but widely used |
| 47 | + * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). |
| 48 | + */ |
| 49 | + protected static $trustedHeaders = array( |
| 50 | + self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', |
| 51 | + self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', |
| 52 | + self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO', |
| 53 | + self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT', |
| 54 | + ); |
34 | 55 |
|
35 | 56 | protected static $httpMethodParameterOverride = false;
|
36 | 57 |
|
@@ -441,14 +462,50 @@ public function overrideGlobals()
|
441 | 462 | /**
|
442 | 463 | * Trusts $_SERVER entries coming from proxies.
|
443 | 464 | *
|
444 |
| - * You should only call this method if your application |
445 |
| - * is hosted behind a reverse proxy that you manage. |
| 465 | + * @deprecated Deprecated since version 2.0, to be removed in 2.3. Use setTrustedProxies instead. |
| 466 | + */ |
| 467 | + public static function trustProxyData() |
| 468 | + { |
| 469 | + self::$trustProxyData = true; |
| 470 | + } |
| 471 | + |
| 472 | + /** |
| 473 | + * Sets a list of trusted proxies. |
| 474 | + * |
| 475 | + * You should only list the reverse proxies that you manage directly. |
| 476 | + * |
| 477 | + * @param array $proxies A list of trusted proxies |
446 | 478 | *
|
447 | 479 | * @api
|
448 | 480 | */
|
449 |
| - public static function trustProxyData() |
| 481 | + public static function setTrustedProxies(array $proxies) |
450 | 482 | {
|
451 |
| - self::$trustProxy = true; |
| 483 | + self::$trustedProxies = $proxies; |
| 484 | + self::$trustProxyData = $proxies ? true : false; |
| 485 | + } |
| 486 | + |
| 487 | + /** |
| 488 | + * Sets the name for trusted headers. |
| 489 | + * |
| 490 | + * The following header keys are supported: |
| 491 | + * |
| 492 | + * * Request::HEADER_CLIENT_IP: defaults to X-Forwarded-For (see getClientIp()) |
| 493 | + * * Request::HEADER_CLIENT_HOST: defaults to X-Forwarded-Host (see getClientHost()) |
| 494 | + * * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getClientPort()) |
| 495 | + * * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure()) |
| 496 | + * |
| 497 | + * Setting an empty value allows to disable the trusted header for the given key. |
| 498 | + * |
| 499 | + * @param string $key The header key |
| 500 | + * @param string $value The header name |
| 501 | + */ |
| 502 | + public static function setTrustedHeaderName($key, $value) |
| 503 | + { |
| 504 | + if (!array_key_exists($key, self::$trustedHeaders)) { |
| 505 | + throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key)); |
| 506 | + } |
| 507 | + |
| 508 | + self::$trustedHeaders[$key] = $value; |
452 | 509 | }
|
453 | 510 |
|
454 | 511 | /**
|
@@ -597,31 +654,43 @@ public function setSession(SessionInterface $session)
|
597 | 654 | /**
|
598 | 655 | * Returns the client IP address.
|
599 | 656 | *
|
| 657 | + * This method can read the client IP address from the "X-Forwarded-For" header |
| 658 | + * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For" |
| 659 | + * header value is a comma+space separated list of IP addresses, the left-most |
| 660 | + * being the original client, and each successive proxy that passed the request |
| 661 | + * adding the IP address where it received the request from. |
| 662 | + * |
| 663 | + * If your reverse proxy uses a different header name than "X-Forwarded-For", |
| 664 | + * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with |
| 665 | + * the "client-ip" key. |
| 666 | + * |
600 | 667 | * @return string The client IP address
|
601 | 668 | *
|
| 669 | + * @see http://en.wikipedia.org/wiki/X-Forwarded-For |
| 670 | + * |
| 671 | + * @deprecated The proxy argument is deprecated since version 2.0 and will be removed in 2.3. Use setTrustedProxies instead. |
| 672 | + * |
602 | 673 | * @api
|
603 | 674 | */
|
604 | 675 | public function getClientIp()
|
605 | 676 | {
|
606 |
| - if (self::$trustProxy) { |
607 |
| - if ($this->server->has('HTTP_CLIENT_IP')) { |
608 |
| - return $this->server->get('HTTP_CLIENT_IP'); |
609 |
| - } elseif ($this->server->has('HTTP_X_FORWARDED_FOR')) { |
610 |
| - $clientIp = explode(',', $this->server->get('HTTP_X_FORWARDED_FOR')); |
611 |
| - |
612 |
| - foreach ($clientIp as $ipAddress) { |
613 |
| - $cleanIpAddress = trim($ipAddress); |
| 677 | + $ip = $this->server->get('REMOTE_ADDR'); |
614 | 678 |
|
615 |
| - if (false !== filter_var($cleanIpAddress, FILTER_VALIDATE_IP)) { |
616 |
| - return $cleanIpAddress; |
617 |
| - } |
618 |
| - } |
| 679 | + if (!self::$trustProxyData) { |
| 680 | + return $ip; |
| 681 | + } |
619 | 682 |
|
620 |
| - return ''; |
621 |
| - } |
| 683 | + if (!self::$trustedHeaders[self::HEADER_CLIENT_IP] || !$this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { |
| 684 | + return $ip; |
622 | 685 | }
|
623 | 686 |
|
624 |
| - return $this->server->get('REMOTE_ADDR'); |
| 687 | + $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); |
| 688 | + $clientIps[] = $ip; |
| 689 | + |
| 690 | + $trustedProxies = self::$trustProxyData && !self::$trustedProxies ? array($ip) : self::$trustedProxies; |
| 691 | + $clientIps = array_diff($clientIps, $trustedProxies); |
| 692 | + |
| 693 | + return array_pop($clientIps); |
625 | 694 | }
|
626 | 695 |
|
627 | 696 | /**
|
@@ -720,14 +789,22 @@ public function getScheme()
|
720 | 789 | /**
|
721 | 790 | * Returns the port on which the request is made.
|
722 | 791 | *
|
| 792 | + * This method can read the client port from the "X-Forwarded-Port" header |
| 793 | + * when trusted proxies were set via "setTrustedProxies()". |
| 794 | + * |
| 795 | + * The "X-Forwarded-Port" header must contain the client port. |
| 796 | + * |
| 797 | + * If your reverse proxy uses a different header name than "X-Forwarded-Port", |
| 798 | + * configure it via "setTrustedHeaderName()" with the "client-port" key. |
| 799 | + * |
723 | 800 | * @return string
|
724 | 801 | *
|
725 | 802 | * @api
|
726 | 803 | */
|
727 | 804 | public function getPort()
|
728 | 805 | {
|
729 |
| - if (self::$trustProxy && $this->headers->has('X-Forwarded-Port')) { |
730 |
| - return $this->headers->get('X-Forwarded-Port'); |
| 806 | + if (self::$trustProxyData && self::$trustedHeaders[self::HEADER_CLIENT_PORT] && $port = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PORT])) { |
| 807 | + return $port; |
731 | 808 | }
|
732 | 809 |
|
733 | 810 | return $this->server->get('SERVER_PORT');
|
@@ -873,31 +950,46 @@ public function getQueryString()
|
873 | 950 | /**
|
874 | 951 | * Checks whether the request is secure or not.
|
875 | 952 | *
|
| 953 | + * This method can read the client port from the "X-Forwarded-Proto" header |
| 954 | + * when trusted proxies were set via "setTrustedProxies()". |
| 955 | + * |
| 956 | + * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". |
| 957 | + * |
| 958 | + * If your reverse proxy uses a different header name than "X-Forwarded-Proto" |
| 959 | + * ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with |
| 960 | + * the "client-proto" key. |
| 961 | + * |
876 | 962 | * @return Boolean
|
877 | 963 | *
|
878 | 964 | * @api
|
879 | 965 | */
|
880 | 966 | public function isSecure()
|
881 | 967 | {
|
882 |
| - return ( |
883 |
| - (strtolower($this->server->get('HTTPS')) == 'on' || $this->server->get('HTTPS') == 1) |
884 |
| - || |
885 |
| - (self::$trustProxy && strtolower($this->headers->get('SSL_HTTPS')) == 'on' || $this->headers->get('SSL_HTTPS') == 1) |
886 |
| - || |
887 |
| - (self::$trustProxy && strtolower($this->headers->get('X_FORWARDED_PROTO')) == 'https') |
888 |
| - ); |
| 968 | + if (self::$trustProxyData && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && $proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO])) { |
| 969 | + return in_array(strtolower($proto), array('https', 'on', '1')); |
| 970 | + } |
| 971 | + |
| 972 | + return 'on' == strtolower($this->server->get('HTTPS')) || 1 == $this->server->get('HTTPS'); |
889 | 973 | }
|
890 | 974 |
|
891 | 975 | /**
|
892 | 976 | * Returns the host name.
|
893 | 977 | *
|
| 978 | + * This method can read the client port from the "X-Forwarded-Host" header |
| 979 | + * when trusted proxies were set via "setTrustedProxies()". |
| 980 | + * |
| 981 | + * The "X-Forwarded-Host" header must contain the client host name. |
| 982 | + * |
| 983 | + * If your reverse proxy uses a different header name than "X-Forwarded-Host", |
| 984 | + * configure it via "setTrustedHeaderName()" with the "client-host" key. |
| 985 | + * |
894 | 986 | * @return string
|
895 | 987 | *
|
896 | 988 | * @api
|
897 | 989 | */
|
898 | 990 | public function getHost()
|
899 | 991 | {
|
900 |
| - if (self::$trustProxy && $host = $this->headers->get('X_FORWARDED_HOST')) { |
| 992 | + if (self::$trustProxyData && self::$trustedHeaders[self::HEADER_CLIENT_HOST] && $host = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_HOST])) { |
901 | 993 | $elements = explode(',', $host);
|
902 | 994 |
|
903 | 995 | $host = trim($elements[count($elements) - 1]);
|
|
0 commit comments