Skip to content

Commit

Permalink
Merge branch 'validate-host-header' into 2.12.x
Browse files Browse the repository at this point in the history
Fix #97
  • Loading branch information
Ocramius committed Jul 6, 2022
2 parents 8366949 + 238bf52 commit 54d4682
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 5 deletions.
3 changes: 1 addition & 2 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,8 @@
</file>
<file src="src/ServerRequestFactory.php">
<LessSpecificReturnStatement occurrences="1"/>
<MixedArgument occurrences="2">
<MixedArgument occurrences="1">
<code>$headers['cookie']</code>
<code>$host</code>
</MixedArgument>
<MixedArgumentTypeCoercion occurrences="1">
<code>$headers</code>
Expand Down
15 changes: 12 additions & 3 deletions src/ServerRequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,15 @@ private static function marshalHostAndPort(array $server, array $headers) : arra

$host = self::getHeaderFromArray('host', $headers, false);
if ($host !== false) {
return self::marshalHostAndPortFromHeader($host);
// Ignore obviously malformed host headers:
// - Whitespace is invalid within a hostname and break the URI representation within HTTP.
// non-printable characters other than SPACE and TAB are already rejected by HeaderSecurity.
// - A comma indicates that multiple host headers have been sent which is not legal
// and might be used in an attack where a load balancer sees a different host header
// than Diactoros.
if (! \preg_match('/[\\t ,]/', $host)) {
return self::marshalHostAndPortFromHeader($host);
}
}

if (! isset($server['SERVER_NAME'])) {
Expand Down Expand Up @@ -289,9 +297,10 @@ private static function marshalHostAndPortFromHeader($host): array
/**
* Retrieve a header value from an array of headers using a case-insensitive lookup.
*
* @template T
* @param array<string, string|list<string>> $headers Key/value header pairs
* @param mixed $default Default value to return if header not found
* @return mixed
* @param T $default Default value to return if header not found
* @return string|T
*/
private static function getHeaderFromArray(string $name, array $headers, $default = null)
{
Expand Down
36 changes: 36 additions & 0 deletions test/ServerRequestFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -786,4 +786,40 @@ public function testHonorsHostHeaderOverServerNameWhenMarshalingUrl(): void
$uri = $request->getUri();
$this->assertSame('example.com', $uri->getHost());
}

/**
* @psalm-return iterable<string, array{
* 0: string
* }>
*/
public function invalidHostHeaders(): iterable
{
return [
'comma' => ['example.com,example.net'],
'space' => ['example com'],
'tab' => ["example\tcom"],
];
}

/**
* @dataProvider invalidHostHeaders
*/
public function testRejectsDuplicatedHostHeader(string $host): void
{
$server = [
'HTTP_HOST' => $host,
];

$request = ServerRequestFactory::fromGlobals(
$server,
null,
null,
null,
null,
new DoNotFilter()
);

$uri = $request->getUri();
$this->assertSame('', $uri->getHost());
}
}

0 comments on commit 54d4682

Please sign in to comment.