Skip to content

Commit c18532a

Browse files
committed
Security fix for proxying individual X-Forwarded-* headers.
1 parent 83cb83f commit c18532a

File tree

2 files changed

+107
-2
lines changed

2 files changed

+107
-2
lines changed

src/TrustProxies.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,12 @@ protected function getTrustedHeaderNames()
119119
case Request::HEADER_FORWARDED:
120120
return Request::HEADER_FORWARDED;
121121
break;
122-
default:
122+
case 'HEADER_X_FORWARDED_ALL':
123+
case Request::HEADER_X_FORWARDED_ALL:
123124
return Request::HEADER_X_FORWARDED_ALL;
125+
break;
124126
}
125127

126-
// Should never reach this point
127128
return $headers;
128129
}
129130
}

tests/TrustedProxyTest.php

+104
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,110 @@ public function test_can_distrust_headers()
180180
});
181181
}
182182

183+
/**
184+
* Test that only the X-Forwarded-For header is trusted.
185+
*/
186+
public function test_x_forwarded_for_header_only_trusted()
187+
{
188+
$trustedProxy = $this->createTrustedProxy(Request::HEADER_X_FORWARDED_FOR, '*');
189+
190+
$request = $this->createProxiedRequest();
191+
192+
$trustedProxy->handle($request, function ($request) {
193+
$this->assertEquals('173.174.200.38', $request->getClientIp(),
194+
'Assert trusted proxy used forwarded header for IP');
195+
$this->assertEquals('http', $request->getScheme(),
196+
'Assert trusted proxy did not use forwarded header for scheme');
197+
$this->assertEquals('localhost', $request->getHost(),
198+
'Assert trusted proxy did not use forwarded header for host');
199+
$this->assertEquals(8888, $request->getPort(), 'Assert trusted proxy did not use forwarded header for port');
200+
});
201+
}
202+
203+
/**
204+
* Test that only the X-Forwarded-Host header is trusted.
205+
*/
206+
public function test_x_forwarded_host_header_only_trusted()
207+
{
208+
$trustedProxy = $this->createTrustedProxy(Request::HEADER_X_FORWARDED_HOST, '*');
209+
210+
$request = $this->createProxiedRequest(['HTTP_X_FORWARDED_HOST' => 'serversforhackers.com:8888']);
211+
212+
$trustedProxy->handle($request, function ($request) {
213+
$this->assertEquals('192.168.10.10', $request->getClientIp(),
214+
'Assert trusted proxy did not use forwarded header for IP');
215+
$this->assertEquals('http', $request->getScheme(),
216+
'Assert trusted proxy did not use forwarded header for scheme');
217+
$this->assertEquals('serversforhackers.com', $request->getHost(),
218+
'Assert trusted proxy used forwarded header for host');
219+
$this->assertEquals(8888, $request->getPort(), 'Assert trusted proxy did not use forwarded header for port');
220+
});
221+
}
222+
223+
/**
224+
* Test that only the X-Forwarded-Port header is trusted.
225+
*/
226+
public function test_x_forwarded_port_header_only_trusted()
227+
{
228+
$trustedProxy = $this->createTrustedProxy(Request::HEADER_X_FORWARDED_PORT, '*');
229+
230+
$request = $this->createProxiedRequest();
231+
232+
$trustedProxy->handle($request, function ($request) {
233+
$this->assertEquals('192.168.10.10', $request->getClientIp(),
234+
'Assert trusted proxy did not use forwarded header for IP');
235+
$this->assertEquals('http', $request->getScheme(),
236+
'Assert trusted proxy did not use forwarded header for scheme');
237+
$this->assertEquals('localhost', $request->getHost(),
238+
'Assert trusted proxy did not use forwarded header for host');
239+
$this->assertEquals(443, $request->getPort(), 'Assert trusted proxy used forwarded header for port');
240+
});
241+
}
242+
243+
/**
244+
* Test that only the X-Forwarded-Proto header is trusted.
245+
*/
246+
public function test_x_forwarded_proto_header_only_trusted()
247+
{
248+
$trustedProxy = $this->createTrustedProxy(Request::HEADER_X_FORWARDED_PROTO, '*');
249+
250+
$request = $this->createProxiedRequest();
251+
252+
$trustedProxy->handle($request, function ($request) {
253+
$this->assertEquals('192.168.10.10', $request->getClientIp(),
254+
'Assert trusted proxy did not use forwarded header for IP');
255+
$this->assertEquals('https', $request->getScheme(),
256+
'Assert trusted proxy used forwarded header for scheme');
257+
$this->assertEquals('localhost', $request->getHost(),
258+
'Assert trusted proxy did not use forwarded header for host');
259+
$this->assertEquals(8888, $request->getPort(), 'Assert trusted proxy did not use forwarded header for port');
260+
});
261+
}
262+
263+
/**
264+
* Test a combination of individual X-Forwarded-* headers are trusted.
265+
*/
266+
public function test_x_forwarded_multiple_individual_headers_trusted()
267+
{
268+
$trustedProxy = $this->createTrustedProxy(
269+
Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST |
270+
Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO,
271+
'*'
272+
);
273+
274+
$request = $this->createProxiedRequest();
275+
276+
$trustedProxy->handle($request, function ($request) {
277+
$this->assertEquals('173.174.200.38', $request->getClientIp(),
278+
'Assert trusted proxy did not use forwarded header for IP');
279+
$this->assertEquals('https', $request->getScheme(),
280+
'Assert trusted proxy used forwarded header for scheme');
281+
$this->assertEquals('serversforhackers.com', $request->getHost(),
282+
'Assert trusted proxy used forwarded header for host');
283+
$this->assertEquals(443, $request->getPort(), 'Assert trusted proxy used forwarded header for port');
284+
});
285+
}
286+
183287
/**
184288
* Test to ensure it's reading text-based configurations and converting it correctly.
185289
*/

0 commit comments

Comments
 (0)