|
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 | /**
|
36 | 57 | * @var \Symfony\Component\HttpFoundation\ParameterBag
|
@@ -439,14 +460,50 @@ public function overrideGlobals()
|
439 | 460 | /**
|
440 | 461 | * Trusts $_SERVER entries coming from proxies.
|
441 | 462 | *
|
442 |
| - * You should only call this method if your application |
443 |
| - * is hosted behind a reverse proxy that you manage. |
| 463 | + * @deprecated Deprecated since version 2.0, to be removed in 2.3. Use setTrustedProxies instead. |
| 464 | + */ |
| 465 | + public static function trustProxyData() |
| 466 | + { |
| 467 | + self::$trustProxyData = true; |
| 468 | + } |
| 469 | + |
| 470 | + /** |
| 471 | + * Sets a list of trusted proxies. |
| 472 | + * |
| 473 | + * You should only list the reverse proxies that you manage directly. |
| 474 | + * |
| 475 | + * @param array $proxies A list of trusted proxies |
444 | 476 | *
|
445 | 477 | * @api
|
446 | 478 | */
|
447 |
| - public static function trustProxyData() |
| 479 | + public static function setTrustedProxies(array $proxies) |
448 | 480 | {
|
449 |
| - self::$trustProxy = true; |
| 481 | + self::$trustedProxies = $proxies; |
| 482 | + self::$trustProxyData = $proxies ? true : false; |
| 483 | + } |
| 484 | + |
| 485 | + /** |
| 486 | + * Sets the name for trusted headers. |
| 487 | + * |
| 488 | + * The following header keys are supported: |
| 489 | + * |
| 490 | + * * Request::HEADER_CLIENT_IP: defaults to X-Forwarded-For (see getClientIp()) |
| 491 | + * * Request::HEADER_CLIENT_HOST: defaults to X-Forwarded-Host (see getClientHost()) |
| 492 | + * * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getClientPort()) |
| 493 | + * * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure()) |
| 494 | + * |
| 495 | + * Setting an empty value allows to disable the trusted header for the given key. |
| 496 | + * |
| 497 | + * @param string $key The header key |
| 498 | + * @param string $value The header name |
| 499 | + */ |
| 500 | + public static function setTrustedHeaderName($key, $value) |
| 501 | + { |
| 502 | + if (!array_key_exists($key, self::$trustedHeaders)) { |
| 503 | + throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key)); |
| 504 | + } |
| 505 | + |
| 506 | + self::$trustedHeaders[$key] = $value; |
450 | 507 | }
|
451 | 508 |
|
452 | 509 | /**
|
@@ -582,31 +639,43 @@ public function setSession(SessionInterface $session)
|
582 | 639 | /**
|
583 | 640 | * Returns the client IP address.
|
584 | 641 | *
|
| 642 | + * This method can read the client IP address from the "X-Forwarded-For" header |
| 643 | + * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For" |
| 644 | + * header value is a comma+space separated list of IP addresses, the left-most |
| 645 | + * being the original client, and each successive proxy that passed the request |
| 646 | + * adding the IP address where it received the request from. |
| 647 | + * |
| 648 | + * If your reverse proxy uses a different header name than "X-Forwarded-For", |
| 649 | + * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with |
| 650 | + * the "client-ip" key. |
| 651 | + * |
585 | 652 | * @return string The client IP address
|
586 | 653 | *
|
| 654 | + * @see http://en.wikipedia.org/wiki/X-Forwarded-For |
| 655 | + * |
| 656 | + * @deprecated The proxy argument is deprecated since version 2.0 and will be removed in 2.3. Use setTrustedProxies instead. |
| 657 | + * |
587 | 658 | * @api
|
588 | 659 | */
|
589 | 660 | public function getClientIp()
|
590 | 661 | {
|
591 |
| - if (self::$trustProxy) { |
592 |
| - if ($this->server->has('HTTP_CLIENT_IP')) { |
593 |
| - return $this->server->get('HTTP_CLIENT_IP'); |
594 |
| - } elseif ($this->server->has('HTTP_X_FORWARDED_FOR')) { |
595 |
| - $clientIp = explode(',', $this->server->get('HTTP_X_FORWARDED_FOR')); |
596 |
| - |
597 |
| - foreach ($clientIp as $ipAddress) { |
598 |
| - $cleanIpAddress = trim($ipAddress); |
| 662 | + $ip = $this->server->get('REMOTE_ADDR'); |
599 | 663 |
|
600 |
| - if (false !== filter_var($cleanIpAddress, FILTER_VALIDATE_IP)) { |
601 |
| - return $cleanIpAddress; |
602 |
| - } |
603 |
| - } |
| 664 | + if (!self::$trustProxyData) { |
| 665 | + return $ip; |
| 666 | + } |
604 | 667 |
|
605 |
| - return ''; |
606 |
| - } |
| 668 | + if (!self::$trustedHeaders[self::HEADER_CLIENT_IP] || !$this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { |
| 669 | + return $ip; |
607 | 670 | }
|
608 | 671 |
|
609 |
| - return $this->server->get('REMOTE_ADDR'); |
| 672 | + $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); |
| 673 | + $clientIps[] = $ip; |
| 674 | + |
| 675 | + $trustedProxies = self::$trustProxyData && !self::$trustedProxies ? array($ip) : self::$trustedProxies; |
| 676 | + $clientIps = array_diff($clientIps, $trustedProxies); |
| 677 | + |
| 678 | + return array_pop($clientIps); |
610 | 679 | }
|
611 | 680 |
|
612 | 681 | /**
|
@@ -705,14 +774,22 @@ public function getScheme()
|
705 | 774 | /**
|
706 | 775 | * Returns the port on which the request is made.
|
707 | 776 | *
|
| 777 | + * This method can read the client port from the "X-Forwarded-Port" header |
| 778 | + * when trusted proxies were set via "setTrustedProxies()". |
| 779 | + * |
| 780 | + * The "X-Forwarded-Port" header must contain the client port. |
| 781 | + * |
| 782 | + * If your reverse proxy uses a different header name than "X-Forwarded-Port", |
| 783 | + * configure it via "setTrustedHeaderName()" with the "client-port" key. |
| 784 | + * |
708 | 785 | * @return string
|
709 | 786 | *
|
710 | 787 | * @api
|
711 | 788 | */
|
712 | 789 | public function getPort()
|
713 | 790 | {
|
714 |
| - if (self::$trustProxy && $this->headers->has('X-Forwarded-Port')) { |
715 |
| - return $this->headers->get('X-Forwarded-Port'); |
| 791 | + if (self::$trustProxyData && self::$trustedHeaders[self::HEADER_CLIENT_PORT] && $port = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PORT])) { |
| 792 | + return $port; |
716 | 793 | }
|
717 | 794 |
|
718 | 795 | return $this->server->get('SERVER_PORT');
|
@@ -858,31 +935,46 @@ public function getQueryString()
|
858 | 935 | /**
|
859 | 936 | * Checks whether the request is secure or not.
|
860 | 937 | *
|
| 938 | + * This method can read the client port from the "X-Forwarded-Proto" header |
| 939 | + * when trusted proxies were set via "setTrustedProxies()". |
| 940 | + * |
| 941 | + * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". |
| 942 | + * |
| 943 | + * If your reverse proxy uses a different header name than "X-Forwarded-Proto" |
| 944 | + * ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with |
| 945 | + * the "client-proto" key. |
| 946 | + * |
861 | 947 | * @return Boolean
|
862 | 948 | *
|
863 | 949 | * @api
|
864 | 950 | */
|
865 | 951 | public function isSecure()
|
866 | 952 | {
|
867 |
| - return ( |
868 |
| - (strtolower($this->server->get('HTTPS')) == 'on' || $this->server->get('HTTPS') == 1) |
869 |
| - || |
870 |
| - (self::$trustProxy && strtolower($this->headers->get('SSL_HTTPS')) == 'on' || $this->headers->get('SSL_HTTPS') == 1) |
871 |
| - || |
872 |
| - (self::$trustProxy && strtolower($this->headers->get('X_FORWARDED_PROTO')) == 'https') |
873 |
| - ); |
| 953 | + if (self::$trustProxyData && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && $proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO])) { |
| 954 | + return in_array(strtolower($proto), array('https', 'on', '1')); |
| 955 | + } |
| 956 | + |
| 957 | + return 'on' == strtolower($this->server->get('HTTPS')) || 1 == $this->server->get('HTTPS'); |
874 | 958 | }
|
875 | 959 |
|
876 | 960 | /**
|
877 | 961 | * Returns the host name.
|
878 | 962 | *
|
| 963 | + * This method can read the client port from the "X-Forwarded-Host" header |
| 964 | + * when trusted proxies were set via "setTrustedProxies()". |
| 965 | + * |
| 966 | + * The "X-Forwarded-Host" header must contain the client host name. |
| 967 | + * |
| 968 | + * If your reverse proxy uses a different header name than "X-Forwarded-Host", |
| 969 | + * configure it via "setTrustedHeaderName()" with the "client-host" key. |
| 970 | + * |
879 | 971 | * @return string
|
880 | 972 | *
|
881 | 973 | * @api
|
882 | 974 | */
|
883 | 975 | public function getHost()
|
884 | 976 | {
|
885 |
| - if (self::$trustProxy && $host = $this->headers->get('X_FORWARDED_HOST')) { |
| 977 | + if (self::$trustProxyData && self::$trustedHeaders[self::HEADER_CLIENT_HOST] && $host = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_HOST])) { |
886 | 978 | $elements = explode(',', $host);
|
887 | 979 |
|
888 | 980 | $host = trim($elements[count($elements) - 1]);
|
|
0 commit comments