@@ -208,7 +208,14 @@ class Request
208
208
protected static $ requestFactory ;
209
209
210
210
private $ isHostValid = true ;
211
- private $ isClientIpsValid = true ;
211
+ private $ isForwardedValid = true ;
212
+
213
+ private static $ forwardedParams = array (
214
+ self ::HEADER_CLIENT_IP => 'for ' ,
215
+ self ::HEADER_CLIENT_HOST => 'host ' ,
216
+ self ::HEADER_CLIENT_PROTO => 'proto ' ,
217
+ self ::HEADER_CLIENT_PORT => 'by ' ,
218
+ );
212
219
213
220
/**
214
221
* Constructor.
@@ -548,11 +555,32 @@ public function overrideGlobals()
548
555
*
549
556
* You should only list the reverse proxies that you manage directly.
550
557
*
551
- * @param array $proxies A list of trusted proxies
558
+ * @param array $proxies A list of trusted proxies
559
+ * @param string $trustedHeader Either Request::HEADER_FORWARDED or Request::HEADER_CLIENT_IP to set which header to trust from your proxies
560
+ *
561
+ * @throws \InvalidArgumentException When $trustedHeader is invalid
552
562
*/
553
- public static function setTrustedProxies (array $ proxies )
563
+ public static function setTrustedProxies (array $ proxies/*, $trustedHeader*/ )
554
564
{
555
565
self ::$ trustedProxies = $ proxies ;
566
+
567
+ if (2 > func_num_args ()) {
568
+ @trigger_error (sprintf ('The %s() method expects Request::HEADER_FORWARDED or Request::HEADER_CLIENT_IP as second argument. Not passing it is deprecated since version 3.3 and will be required in 4.0. ' , __METHOD__ ));
569
+
570
+ return ;
571
+ }
572
+ $ trustedHeader = func_get_arg (1 );
573
+
574
+ if (self ::HEADER_FORWARDED === $ trustedHeader ) {
575
+ self ::$ trustedHeaders [self ::HEADER_CLIENT_IP ] = null ;
576
+ self ::$ trustedHeaders [self ::HEADER_CLIENT_HOST ] = null ;
577
+ self ::$ trustedHeaders [self ::HEADER_CLIENT_PORT ] = null ;
578
+ self ::$ trustedHeaders [self ::HEADER_CLIENT_PROTO ] = null ;
579
+ } elseif (self ::HEADER_CLIENT_IP === $ trustedHeader ) {
580
+ self ::$ trustedHeaders [self ::HEADER_FORWARDED ] = null ;
581
+ } else {
582
+ throw new \InvalidArgumentException (sprintf ('Invalid trusted header argument: Request::HEADER_FORWARDED or Request::HEADER_CLIENT_IP was expected, "%s" given. ' , $ trustedHeader ));
583
+ }
556
584
}
557
585
558
586
/**
@@ -797,46 +825,13 @@ public function setSession(SessionInterface $session)
797
825
*/
798
826
public function getClientIps ()
799
827
{
800
- $ clientIps = array ();
801
828
$ ip = $ this ->server ->get ('REMOTE_ADDR ' );
802
829
803
830
if (!$ this ->isFromTrustedProxy ()) {
804
831
return array ($ ip );
805
832
}
806
833
807
- $ hasTrustedForwardedHeader = self ::$ trustedHeaders [self ::HEADER_FORWARDED ] && $ this ->headers ->has (self ::$ trustedHeaders [self ::HEADER_FORWARDED ]);
808
- $ hasTrustedClientIpHeader = self ::$ trustedHeaders [self ::HEADER_CLIENT_IP ] && $ this ->headers ->has (self ::$ trustedHeaders [self ::HEADER_CLIENT_IP ]);
809
-
810
- if ($ hasTrustedForwardedHeader ) {
811
- $ forwardedHeader = $ this ->headers ->get (self ::$ trustedHeaders [self ::HEADER_FORWARDED ]);
812
- preg_match_all ('{(for)=("?\[?)([a-z0-9\.:_\-/]*)} ' , $ forwardedHeader , $ matches );
813
- $ forwardedClientIps = $ matches [3 ];
814
-
815
- $ forwardedClientIps = $ this ->normalizeAndFilterClientIps ($ forwardedClientIps , $ ip );
816
- $ clientIps = $ forwardedClientIps ;
817
- }
818
-
819
- if ($ hasTrustedClientIpHeader ) {
820
- $ xForwardedForClientIps = array_map ('trim ' , explode (', ' , $ this ->headers ->get (self ::$ trustedHeaders [self ::HEADER_CLIENT_IP ])));
821
-
822
- $ xForwardedForClientIps = $ this ->normalizeAndFilterClientIps ($ xForwardedForClientIps , $ ip );
823
- $ clientIps = $ xForwardedForClientIps ;
824
- }
825
-
826
- if ($ hasTrustedForwardedHeader && $ hasTrustedClientIpHeader && $ forwardedClientIps !== $ xForwardedForClientIps ) {
827
- if (!$ this ->isClientIpsValid ) {
828
- return array ('0.0.0.0 ' );
829
- }
830
- $ this ->isClientIpsValid = false ;
831
-
832
- 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 your project to distrust one of them. ' );
833
- }
834
-
835
- if (!$ hasTrustedForwardedHeader && !$ hasTrustedClientIpHeader ) {
836
- return $ this ->normalizeAndFilterClientIps (array (), $ ip );
837
- }
838
-
839
- return $ clientIps ;
834
+ return $ this ->getTrustedValues (self ::HEADER_CLIENT_IP , $ ip ) ?: array ($ ip );
840
835
}
841
836
842
837
/**
@@ -962,31 +957,25 @@ public function getScheme()
962
957
*/
963
958
public function getPort ()
964
959
{
965
- if ($ this ->isFromTrustedProxy ()) {
966
- if (self ::$ trustedHeaders [self ::HEADER_CLIENT_PORT ] && $ port = $ this ->headers ->get (self ::$ trustedHeaders [self ::HEADER_CLIENT_PORT ])) {
967
- return $ port ;
968
- }
969
-
970
- if (self ::$ trustedHeaders [self ::HEADER_CLIENT_PROTO ] && 'https ' === $ this ->headers ->get (self ::$ trustedHeaders [self ::HEADER_CLIENT_PROTO ], 'http ' )) {
971
- return 443 ;
972
- }
960
+ if ($ this ->isFromTrustedProxy () && $ host = $ this ->getTrustedValues (self ::HEADER_CLIENT_PORT )) {
961
+ $ host = $ host [0 ];
962
+ } elseif ($ this ->isFromTrustedProxy () && $ host = $ this ->getTrustedValues (self ::HEADER_CLIENT_HOST )) {
963
+ $ host = $ host [0 ];
964
+ } elseif (!$ host = $ this ->headers ->get ('HOST ' )) {
965
+ return $ this ->server ->get ('SERVER_PORT ' );
973
966
}
974
967
975
- if ($ host = $ this ->headers ->get ('HOST ' )) {
976
- if ($ host [0 ] === '[ ' ) {
977
- $ pos = strpos ($ host , ': ' , strrpos ($ host , '] ' ));
978
- } else {
979
- $ pos = strrpos ($ host , ': ' );
980
- }
981
-
982
- if (false !== $ pos ) {
983
- return (int ) substr ($ host , $ pos + 1 );
984
- }
968
+ if ($ host [0 ] === '[ ' ) {
969
+ $ pos = strpos ($ host , ': ' , strrpos ($ host , '] ' ));
970
+ } else {
971
+ $ pos = strrpos ($ host , ': ' );
972
+ }
985
973
986
- return 'https ' === $ this ->getScheme () ? 443 : 80 ;
974
+ if (false !== $ pos ) {
975
+ return (int ) substr ($ host , $ pos + 1 );
987
976
}
988
977
989
- return $ this ->server -> get ( ' SERVER_PORT ' ) ;
978
+ return ' https ' === $ this ->getScheme () ? 443 : 80 ;
990
979
}
991
980
992
981
/**
@@ -1186,8 +1175,8 @@ public function getQueryString()
1186
1175
*/
1187
1176
public function isSecure ()
1188
1177
{
1189
- if ($ this ->isFromTrustedProxy () && self :: $ trustedHeaders [ self :: HEADER_CLIENT_PROTO ] && $ proto = $ this ->headers -> get (self ::$ trustedHeaders [ self :: HEADER_CLIENT_PROTO ] )) {
1190
- return in_array (strtolower (current ( explode ( ' , ' , $ proto))) , array ('https ' , 'on ' , 'ssl ' , '1 ' ));
1178
+ if ($ this ->isFromTrustedProxy () && $ proto = $ this ->getTrustedValues (self ::HEADER_CLIENT_PROTO )) {
1179
+ return in_array (strtolower ($ proto[ 0 ]) , array ('https ' , 'on ' , 'ssl ' , '1 ' ), true );
1191
1180
}
1192
1181
1193
1182
$ https = $ this ->server ->get ('HTTPS ' );
@@ -1212,10 +1201,8 @@ public function isSecure()
1212
1201
*/
1213
1202
public function getHost ()
1214
1203
{
1215
- if ($ this ->isFromTrustedProxy () && self ::$ trustedHeaders [self ::HEADER_CLIENT_HOST ] && $ host = $ this ->headers ->get (self ::$ trustedHeaders [self ::HEADER_CLIENT_HOST ])) {
1216
- $ elements = explode (', ' , $ host );
1217
-
1218
- $ host = $ elements [count ($ elements ) - 1 ];
1204
+ if ($ this ->isFromTrustedProxy () && $ host = $ this ->getTrustedValues (self ::HEADER_CLIENT_HOST )) {
1205
+ $ host = end ($ host );
1219
1206
} elseif (!$ host = $ this ->headers ->get ('HOST ' )) {
1220
1207
if (!$ host = $ this ->server ->get ('SERVER_NAME ' )) {
1221
1208
$ host = $ this ->server ->get ('SERVER_ADDR ' , '' );
@@ -1998,6 +1985,9 @@ public function isFromTrustedProxy()
1998
1985
1999
1986
private function normalizeAndFilterClientIps (array $ clientIps , $ ip )
2000
1987
{
1988
+ if (!$ clientIps ) {
1989
+ return array ();
1990
+ }
2001
1991
$ clientIps [] = $ ip ; // Complete the IP chain with the IP the request actually came from
2002
1992
$ firstTrustedIp = null ;
2003
1993
@@ -2026,4 +2016,41 @@ private function normalizeAndFilterClientIps(array $clientIps, $ip)
2026
2016
// Now the IP chain contains only untrusted proxies and the client IP
2027
2017
return $ clientIps ? array_reverse ($ clientIps ) : array ($ firstTrustedIp );
2028
2018
}
2019
+
2020
+ private function getTrustedValues ($ type , $ ip = null )
2021
+ {
2022
+ $ clientValues = array ();
2023
+ $ forwardedValues = array ();
2024
+
2025
+ if (self ::$ trustedHeaders [$ type ] && $ this ->headers ->has (self ::$ trustedHeaders [$ type ])) {
2026
+ foreach (explode (', ' , $ this ->headers ->get (self ::$ trustedHeaders [$ type ])) as $ v ) {
2027
+ $ clientValues [] = (self ::HEADER_CLIENT_PORT === $ type ? '0.0.0.0: ' : '' ).trim ($ v );
2028
+ }
2029
+ }
2030
+
2031
+ if (self ::$ trustedHeaders [self ::HEADER_FORWARDED ] && $ this ->headers ->has (self ::$ trustedHeaders [self ::HEADER_FORWARDED ])) {
2032
+ $ forwardedValues = $ this ->headers ->get (self ::$ trustedHeaders [self ::HEADER_FORWARDED ]);
2033
+ $ forwardedValues = preg_match_all (sprintf ('{(?:%s)=(?:"?\[?)([a-zA-Z0-9\.:_\-/]*+)} ' , self ::$ forwardedParams [$ type ]), $ forwardedValues , $ matches ) ? $ matches [1 ] : array ('' );
2034
+ }
2035
+
2036
+ if (null !== $ ip ) {
2037
+ $ clientValues = $ this ->normalizeAndFilterClientIps ($ clientValues , $ ip );
2038
+ $ forwardedValues = $ this ->normalizeAndFilterClientIps ($ forwardedValues , $ ip );
2039
+ }
2040
+
2041
+ if ($ forwardedValues === $ clientValues || !$ clientValues ) {
2042
+ return $ forwardedValues ;
2043
+ }
2044
+
2045
+ if (!$ forwardedValues ) {
2046
+ return $ clientValues ;
2047
+ }
2048
+
2049
+ if (!$ this ->isForwardedValid ) {
2050
+ return null !== $ ip ? array ('0.0.0.0 ' , $ ip ) : array ();
2051
+ }
2052
+ $ this ->isForwardedValid = false ;
2053
+
2054
+ throw new ConflictingHeadersException (sprintf ('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove the "%2$s" header, or configure your project to distrust it. ' , self ::$ trustedHeaders [self ::HEADER_FORWARDED ], self ::$ trustedHeaders [$ type ]));
2055
+ }
2029
2056
}
0 commit comments