Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 6.5.7 #3022

Merged
merged 1 commit into from Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,10 @@
# Change Log

## 6.5.7 - 2022-06-09

* Fix failure to strip Authorization header on HTTP downgrade
* Fix failure to strip the Cookie header on change in host or HTTP downgrade

## 6.5.6 - 2022-05-25

* Fix cross-domain cookie leakage
Expand Down
38 changes: 32 additions & 6 deletions src/RedirectMiddleware.php
Expand Up @@ -141,7 +141,7 @@ function (ResponseInterface $response) use ($uri, $statusCode) {
}

/**
* Check for too many redirects
* Check for too many redirects.
*
* @return void
*
Expand Down Expand Up @@ -190,7 +190,7 @@ public function modifyRequest(
$modify['body'] = '';
}

$uri = $this->redirectUri($request, $response, $protocols);
$uri = self::redirectUri($request, $response, $protocols);
if (isset($options['idn_conversion']) && ($options['idn_conversion'] !== false)) {
$idnOptions = ($options['idn_conversion'] === true) ? IDNA_DEFAULT : $options['idn_conversion'];
$uri = Utils::idnUriConvert($uri, $idnOptions);
Expand All @@ -210,24 +210,50 @@ public function modifyRequest(
$modify['remove_headers'][] = 'Referer';
}

// Remove Authorization header if host is different.
if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
// Remove Authorization and Cookie headers if required.
if (self::shouldStripSensitiveHeaders($request->getUri(), $modify['uri'])) {
$modify['remove_headers'][] = 'Authorization';
$modify['remove_headers'][] = 'Cookie';
}

return Psr7\modify_request($request, $modify);
}

/**
* Set the appropriate URL on the request based on the location header
* Determine if we should strip sensitive headers from the request.
*
* We return true if either of the following conditions are true:
*
* 1. the host is different;
* 2. the scheme has changed, and now is non-https.
*
* @return bool
*/
private static function shouldStripSensitiveHeaders(
UriInterface $originalUri,
UriInterface $modifiedUri
) {
if (strcasecmp($originalUri->getHost(), $modifiedUri->getHost()) !== 0) {
return true;
}

if ($originalUri->getScheme() !== $modifiedUri->getScheme() && 'https' !== $modifiedUri->getScheme()) {
return true;
}

return false;
}

/**
* Set the appropriate URL on the request based on the location header.
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @param array $protocols
*
* @return UriInterface
*/
private function redirectUri(
private static function redirectUri(
RequestInterface $request,
ResponseInterface $response,
array $protocols
Expand Down
37 changes: 21 additions & 16 deletions tests/RedirectMiddlewareTest.php
Expand Up @@ -251,31 +251,36 @@ public function testInvokesOnRedirectForRedirects()
self::assertTrue($call);
}

public function testRemoveAuthorizationHeaderOnRedirect()
public function crossOriginRedirectProvider()
{
$mock = new MockHandler([
new Response(302, ['Location' => 'http://test.com']),
function (RequestInterface $request) {
self::assertFalse($request->hasHeader('Authorization'));
return new Response(200);
}
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('http://example.com?a=b', ['auth' => ['testuser', 'testpass']]);
return [
['http://example.com?a=b', 'http://test.com/', false],
['https://example.com?a=b', 'https://test.com/', false],
['http://example.com?a=b', 'https://test.com/', false],
['https://example.com?a=b', 'http://test.com/', false],
['http://example.com?a=b', 'http://example.com/', true],
['https://example.com?a=b', 'https://example.com/', true],
['http://example.com?a=b', 'https://example.com/', true],
['https://example.com?a=b', 'http://example.com/', false],
];
}

public function testNotRemoveAuthorizationHeaderOnRedirect()
/**
* @dataProvider crossOriginRedirectProvider
*/
public function testHeadersTreatmentOnRedirect($originalUri, $targetUri, $shouldBePresent)
{
$mock = new MockHandler([
new Response(302, ['Location' => 'http://example.com/2']),
function (RequestInterface $request) {
self::assertTrue($request->hasHeader('Authorization'));
new Response(302, ['Location' => $targetUri]),
function (RequestInterface $request) use ($shouldBePresent) {
self::assertSame($shouldBePresent, $request->hasHeader('Authorization'));
self::assertSame($shouldBePresent, $request->hasHeader('Cookie'));

return new Response(200);
}
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('http://example.com?a=b', ['auth' => ['testuser', 'testpass']]);
$client->get($originalUri, ['auth' => ['testuser', 'testpass'], 'headers' => ['Cookie' => 'foo=bar']]);
}
}