Skip to content
This repository was archived by the owner on Jan 30, 2020. It is now read-only.

Commit 6641f4c

Browse files
committed
Merge branch 'security/zf2018-01'
Fixes ZF2018-01
2 parents b3d847a + b28589c commit 6641f4c

File tree

5 files changed

+205
-32
lines changed

5 files changed

+205
-32
lines changed

CHANGELOG.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,44 @@
22

33
All notable changes to this project will be documented in this file, in reverse chronological order by release.
44

5-
## 2.10.3 - TBD
5+
## 2.10.3 - 2018-08-01
66

77
### Added
88

99
- Nothing.
1010

1111
### Changed
1212

13-
- Nothing.
13+
- This release modifies how `Zend\Feed\Pubsubhubbub\AbstractCallback::_detectCallbackUrl()`
14+
marshals the request URI. In prior releases, we would attempt to inspect the
15+
`X-Rewrite-Url` and `X-Original-Url` headers, using their values, if present.
16+
These headers are issued by the ISAPI_Rewrite module for IIS (developed by
17+
HeliconTech). However, we have no way of guaranteeing that the module is what
18+
issued the headers, making it an unreliable source for discovering the URI. As
19+
such, we have removed this feature in this release.
20+
21+
The method is not called internally. If you are calling the method from your
22+
own extension and need support for ISAPI_Rewrite, you will need to override
23+
the method as follows:
24+
25+
```php
26+
protected function _detectCallbackUrl()
27+
{
28+
$callbackUrl = null;
29+
if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
30+
$callbackUrl = $_SERVER['HTTP_X_REWRITE_URL'];
31+
}
32+
if (isset($_SERVER['HTTP_X_ORIGINAL_URL'])) {
33+
$callbackUrl = $_SERVER['HTTP_X_ORIGINAL_URL'];
34+
}
35+
36+
return $callbackUrl ?: parent::__detectCallbackUrl();
37+
}
38+
```
39+
40+
If you use an approach such as the above, make sure you also instruct your web
41+
server to strip any incoming headers of the same name so that you can
42+
guarantee they are issued by the ISAPI_Rewrite module.
1443

1544
### Deprecated
1645

phpunit.xml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
33
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
44
bootstrap="vendor/autoload.php"
5+
backupGlobals="true"
56
colors="true">
67
<testsuites>
78
<testsuite name="zend-feed Test Suite">

src/PubSubHubbub/AbstractCallback.php

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -211,28 +211,31 @@ public function getSubscriberCount()
211211
protected function _detectCallbackUrl()
212212
{
213213
// @codingStandardsIgnoreEnd
214-
$callbackUrl = '';
215-
if (isset($_SERVER['HTTP_X_ORIGINAL_URL'])) {
216-
$callbackUrl = $_SERVER['HTTP_X_ORIGINAL_URL'];
217-
} elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
218-
$callbackUrl = $_SERVER['HTTP_X_REWRITE_URL'];
219-
} elseif (isset($_SERVER['REQUEST_URI'])) {
220-
$callbackUrl = $_SERVER['REQUEST_URI'];
221-
$scheme = 'http';
222-
if ($_SERVER['HTTPS'] == 'on') {
223-
$scheme = 'https';
224-
}
225-
$schemeAndHttpHost = $scheme . '://' . $this->_getHttpHost();
226-
if (strpos($callbackUrl, $schemeAndHttpHost) === 0) {
227-
$callbackUrl = substr($callbackUrl, strlen($schemeAndHttpHost));
228-
}
229-
} elseif (isset($_SERVER['ORIG_PATH_INFO'])) {
230-
$callbackUrl = $_SERVER['ORIG_PATH_INFO'];
231-
if (! empty($_SERVER['QUERY_STRING'])) {
232-
$callbackUrl .= '?' . $_SERVER['QUERY_STRING'];
233-
}
214+
$callbackUrl = null;
215+
216+
// IIS7 with URL Rewrite: make sure we get the unencoded url
217+
// (double slash problem).
218+
$iisUrlRewritten = isset($_SERVER['IIS_WasUrlRewritten']) ? $_SERVER['IIS_WasUrlRewritten'] : null;
219+
$unencodedUrl = isset($_SERVER['UNENCODED_URL']) ? $_SERVER['UNENCODED_URL'] : null;
220+
if ('1' == $iisUrlRewritten && ! empty($unencodedUrl)) {
221+
return $unencodedUrl;
234222
}
235-
return $callbackUrl;
223+
224+
// HTTP proxy requests setup request URI with scheme and host [and port]
225+
// + the URL path, only use URL path.
226+
if (isset($_SERVER['REQUEST_URI'])) {
227+
$callbackUrl = $this->buildCallbackUrlFromRequestUri();
228+
}
229+
230+
if (null !== $callbackUrl) {
231+
return $callbackUrl;
232+
}
233+
234+
if (isset($_SERVER['ORIG_PATH_INFO'])) {
235+
return $this->buildCallbackUrlFromOrigPathInfo();
236+
}
237+
238+
return '';
236239
}
237240

238241
/**
@@ -247,19 +250,19 @@ protected function _getHttpHost()
247250
if (! empty($_SERVER['HTTP_HOST'])) {
248251
return $_SERVER['HTTP_HOST'];
249252
}
250-
$scheme = 'http';
251-
if ($_SERVER['HTTPS'] == 'on') {
252-
$scheme = 'https';
253-
}
254-
$name = $_SERVER['SERVER_NAME'];
255-
$port = $_SERVER['SERVER_PORT'];
256-
if (($scheme == 'http' && $port == 80)
257-
|| ($scheme == 'https' && $port == 443)
253+
254+
$https = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : null;
255+
$scheme = $https === 'on' ? 'https' : 'http';
256+
$name = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
257+
$port = isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 80;
258+
259+
if (($scheme === 'http' && $port === 80)
260+
|| ($scheme === 'https' && $port === 443)
258261
) {
259262
return $name;
260263
}
261264

262-
return $name . ':' . $port;
265+
return sprintf('%s:%d', $name, $port);
263266
}
264267

265268
/**
@@ -304,4 +307,38 @@ protected function _getRawBody()
304307

305308
return strlen(trim($body)) > 0 ? $body : false;
306309
}
310+
311+
/**
312+
* Build the callback URL from the REQUEST_URI server parameter.
313+
*
314+
* @return string
315+
*/
316+
private function buildCallbackUrlFromRequestUri()
317+
{
318+
$callbackUrl = $_SERVER['REQUEST_URI'];
319+
$https = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : null;
320+
$scheme = $https === 'on' ? 'https' : 'http';
321+
if ($https === 'on') {
322+
$scheme = 'https';
323+
}
324+
$schemeAndHttpHost = $scheme . '://' . $this->_getHttpHost();
325+
if (strpos($callbackUrl, $schemeAndHttpHost) === 0) {
326+
$callbackUrl = substr($callbackUrl, strlen($schemeAndHttpHost));
327+
}
328+
return $callbackUrl;
329+
}
330+
331+
/**
332+
* Build the callback URL from the ORIG_PATH_INFO server parameter.
333+
*
334+
* @return string
335+
*/
336+
private function buildCallbackUrlFromOrigPathInfo()
337+
{
338+
$callbackUrl = $_SERVER['ORIG_PATH_INFO'];
339+
if (! empty($_SERVER['QUERY_STRING'])) {
340+
$callbackUrl .= '?' . $_SERVER['QUERY_STRING'];
341+
}
342+
return $callbackUrl;
343+
}
307344
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-feed for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-feed/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
namespace ZendTest\Feed\PubSubHubbub;
9+
10+
use PHPUnit\Framework\TestCase;
11+
use ReflectionMethod;
12+
use Zend\Feed\PubSubHubbub\AbstractCallback;
13+
14+
class AbstractCallbackTest extends TestCase
15+
{
16+
public function testDetectCallbackUrlIgnoresXOriginalUrlHeaderWhenXRewriteUrlHeaderIsNotPresent()
17+
{
18+
$_SERVER = array_merge($_SERVER, [
19+
'HTTP_X_ORIGINAL_URL' => '/hijack-attempt',
20+
'HTTPS' => 'on',
21+
'HTTP_HOST' => 'example.com',
22+
'REQUEST_URI' => '/requested/path',
23+
]);
24+
25+
$callback = new TestAsset\Callback();
26+
27+
$r = new ReflectionMethod($callback, '_detectCallbackUrl');
28+
$r->setAccessible(true);
29+
30+
$this->assertSame('/requested/path', $r->invoke($callback));
31+
}
32+
33+
public function testDetectCallbackUrlRequiresCombinationOfIISWasUrlRewrittenAndUnencodedUrlToReturnEarly()
34+
{
35+
$_SERVER = array_merge($_SERVER, [
36+
'IIS_WasUrlRewritten' => '1',
37+
'UNENCODED_URL' => '/requested/path',
38+
]);
39+
40+
$callback = new TestAsset\Callback();
41+
42+
$r = new ReflectionMethod($callback, '_detectCallbackUrl');
43+
$r->setAccessible(true);
44+
45+
$this->assertSame('/requested/path', $r->invoke($callback));
46+
}
47+
48+
public function testDetectCallbackUrlUsesRequestUriWhenNoOtherRewriteHeadersAreFound()
49+
{
50+
$_SERVER = array_merge($_SERVER, [
51+
'REQUEST_URI' => '/expected/path'
52+
]);
53+
54+
$callback = new TestAsset\Callback();
55+
56+
$r = new ReflectionMethod($callback, '_detectCallbackUrl');
57+
$r->setAccessible(true);
58+
59+
$this->assertSame('/expected/path', $r->invoke($callback));
60+
}
61+
62+
public function testDetectCallbackUrlFallsBackToOrigPathInfoWhenAllElseFails()
63+
{
64+
$_SERVER = array_merge($_SERVER, [
65+
'ORIG_PATH_INFO' => '/expected/path',
66+
]);
67+
68+
$callback = new TestAsset\Callback();
69+
70+
$r = new ReflectionMethod($callback, '_detectCallbackUrl');
71+
$r->setAccessible(true);
72+
73+
$this->assertSame('/expected/path', $r->invoke($callback));
74+
}
75+
76+
public function testDetectCallbackReturnsEmptyStringIfNoResourcesMatchedInServerSuperglobal()
77+
{
78+
$callback = new TestAsset\Callback();
79+
80+
$r = new ReflectionMethod($callback, '_detectCallbackUrl');
81+
$r->setAccessible(true);
82+
83+
$this->assertSame('', $r->invoke($callback));
84+
}
85+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-feed for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-feed/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
namespace ZendTest\Feed\PubSubHubbub\TestAsset;
9+
10+
use Zend\Feed\PubSubHubbub\AbstractCallback;
11+
12+
class Callback extends AbstractCallback
13+
{
14+
/**
15+
* {@inheritDoc}
16+
*/
17+
public function handle(array $httpData = null, $sendResponseNow = false)
18+
{
19+
return false;
20+
}
21+
}

0 commit comments

Comments
 (0)