From: Chris Burgess Date: Mon, 13 Jul 2015 00:45:51 +0000 (+1200) Subject: CRM-16832. Do not redirect offsite when fed invalid keys. X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=6ef7bd91a684e23c5d8c601b2a5060f0176a6d7b;p=civicrm-core.git CRM-16832. Do not redirect offsite when fed invalid keys. URLs to test: * http://civicrm.dev/civicrm/contribute/transact?qfKey=xxx&entryURL=http://evil.example.com/ should go to CIVICRM_UF_BASEURL * http://civicrm.dev/civicrm/contribute/transact?qfKey=xxx&entryURL=/civicrm/contribute/transact%3Fid%3D1 should go to /civicrm/contribute/transact?id=1 * http://civicrm.dev/civicrm/contribute/transact?qfKey=xxx&entryURL=http://civicrm.dev/civicrm/contribute/transact%3Fid%3D1 should go to /civicrm/contribute/transact?id=1 --- diff --git a/CRM/Core/Controller.php b/CRM/Core/Controller.php index 7744c60d1f..b07b398af8 100644 --- a/CRM/Core/Controller.php +++ b/CRM/Core/Controller.php @@ -858,18 +858,25 @@ class CRM_Core_Controller extends HTML_QuickForm_Controller { } /** - * Instead of outputting a fatal error message, we'll just redirect to the entryURL if present + * Instead of outputting a fatal error message, we'll just redirect + * to the entryURL if present * * @return void */ public function invalidKeyRedirect() { - if ($this->_entryURL) { - CRM_Core_Session::setStatus(ts('Your browser session has expired and we are unable to complete your form submission. We have returned you to the initial step so you can complete and resubmit the form. If you experience continued difficulties, please contact us for assistance.')); - return CRM_Utils_System::redirect($this->_entryURL); - } - else { - self::invalidKeyCommon(); + if ($this->_entryURL && $url_parts = parse_url($this->_entryURL)) { + // CRM-16832: Ensure local redirects only. + if (!empty($url_parts['path'])) { + // Prepend a slash, but don't duplicate it. + $redirect_url = '/' . ltrim($url_parts['path'], '/'); + if (!empty($url_parts['query'])) { + $redirect_url .= '?' . $url_parts['query']; + } + CRM_Core_Session::setStatus(ts('Your browser session has expired and we are unable to complete your form submission. We have returned you to the initial step so you can complete and resubmit the form. If you experience continued difficulties, please contact us for assistance.')); + return CRM_Utils_System::redirect($redirect_url); + } } + self::invalidKeyCommon(); } } diff --git a/tests/phpunit/WebTest/Utils/RedirectTest.php b/tests/phpunit/WebTest/Utils/RedirectTest.php new file mode 100644 index 0000000000..39d3829302 --- /dev/null +++ b/tests/phpunit/WebTest/Utils/RedirectTest.php @@ -0,0 +1,135 @@ +settings = new CiviSeleniumSettings(); + if (property_exists($this->settings, 'serverStartupTimeOut') && $this->settings->serverStartupTimeOut) { + global $CiviSeleniumTestCase_polled; + if (!$CiviSeleniumTestCase_polled) { + $CiviSeleniumTestCase_polled = TRUE; + CRM_Utils_Network::waitForServiceStartup( + $this->drivers[0]->getHost(), + $this->drivers[0]->getPort(), + $this->settings->serverStartupTimeOut + ); + } + } + } + + protected function setUp() { + parent::setUp(); + //URL should eventually be adapted for multisite + $this->url = $this->settings->sandboxURL; + + $this->ch = curl_init(); + curl_setopt($this->ch, CURLOPT_HEADER, FALSE); + curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, FALSE); + // curl_setopt($this->ch, CURLOPT_ENCODING, 'gzip'); + // curl_setopt($this->ch, CURLOPT_VERBOSE, 0); + } + + /** + * + */ + private function tryRedirect($input_url, $expected_url) { + // file_put_contents('php://stderr', $input_url . "\n", FILE_APPEND); + $url = $this->url . '/' . $input_url; + $expected_url = $this->url . '/' . $expected_url; + curl_setopt($this->ch, CURLOPT_URL, $url); + $req = curl_exec($this->ch); + $this->assertEquals(0, curl_errno($this->ch), 'cURL error: ' . curl_error($this->ch)); + if (!curl_errno($this->ch)) { + $info = curl_getinfo($this->ch); + // file_put_contents('php://stderr', print_r($info,1), FILE_APPEND); + $this->assertEquals($expected_url, $info['redirect_url']); + $this->assertEquals('302', $info['http_code']); + } + } + + /** + * Handle onsite redirects with absolute URL. + */ + public function testAbsoluteOnsiteRedirect() { + $this->tryRedirect("civicrm/contribute/transact?qfKey=xxx&entryURL={$this->url}/civicrm/contribute/transact%3Fid%3D1", 'civicrm/contribute/transact?id=1'); + } + + /** + * Handle onsite redirects with slash prefix and query params. + */ + public function testOnsiteRedirectWithSlashPrefixAndQueryParams() { + $this->tryRedirect('civicrm/contribute/transact?qfKey=xxx&entryURL=/civicrm/contribute/transact%3Fid%3D1', 'civicrm/contribute/transact?id=1'); + } + + /** + * Handle onsite redirects with non-CiviCRM paths. + */ + public function testOtherpathRedirect() { + $this->tryRedirect('civicrm/contribute/transact?qfKey=xxx&entryURL=asdf', 'asdf'); + } + + /** + * Handle offsite redirects without path as onsite redirects. + */ + public function testOffsiteRedirectNoPath() { + $this->tryRedirect('civicrm/contribute/transact?qfKey=xxx&entryURL=http://evil.example.com/', ''); + } + + /** + * Handle offsite redirects with paths as onsite redirects. + */ + public function testOffsiteRedirectWithPath() { + $this->tryRedirect('civicrm/contribute/transact?qfKey=xxx&entryURL=http://evil.example.com/civicrm', 'civicrm'); + } + +}