diff --git a/config-templates/config.php b/config-templates/config.php index a6812d906da6f8f9304761202f63d2864d54d652..fba47fbe175b160520c51f1f6d63f71a4a2c262b 100644 --- a/config-templates/config.php +++ b/config-templates/config.php @@ -668,4 +668,14 @@ $config = array ( */ 'proxy' => NULL, + /* + * Array of URL's to allow a trusted redirect to. + * + * Set to NULL to disable. + * + * Example: + * 'redirect.trustedsites' => array('sp.example.com', 'othersite.org'), + */ + 'redirect.trustedsites' => NULL, + ); diff --git a/lib/SimpleSAML/Auth/Default.php b/lib/SimpleSAML/Auth/Default.php index 2f22b3fab3a0d9b514c2414dde647a7fa937602b..de5873c8ae8feaeec0f5d90331f60be2ddf37e31 100644 --- a/lib/SimpleSAML/Auth/Default.php +++ b/lib/SimpleSAML/Auth/Default.php @@ -251,7 +251,7 @@ class SimpleSAML_Auth_Default { $session = SimpleSAML_Session::getInstance(); $session->doLogin($authId, self::extractPersistentAuthState($state)); - SimpleSAML_Utilities::redirect($redirectTo); + SimpleSAML_Utilities::redirectUntrustedURL($redirectTo); } } diff --git a/lib/SimpleSAML/Utilities.php b/lib/SimpleSAML/Utilities.php index 6a902e092856a4fe9d649192e55743e7a5497440..e9587ccc52969c066cc244fee642ea6c5efcb62a 100644 --- a/lib/SimpleSAML/Utilities.php +++ b/lib/SimpleSAML/Utilities.php @@ -563,14 +563,20 @@ class SimpleSAML_Utilities { * will be urlencoded. If the value is NULL, then the * parameter will be encoded as just the name, without a * value. + * $allowed_redirect_hosts + * Array whitelist of hosts that redirects are allowed for. + * If NULL value, redirect will be allowed to any host. + * Otherwise, $url host must be present in Array for redirect. + * If the host is not present, an exception will be thrown. * * Returns: * This function never returns. */ - public static function redirect($url, $parameters = array()) { + public static function redirect($url, $parameters = array(), $allowed_redirect_hosts = NULL) { assert(is_string($url)); assert(strlen($url) > 0); assert(is_array($parameters)); + if($allowed_redirect_hosts != NULL) assert(is_array($allowed_redirect_hosts)); /* Check for relative URL. */ if(substr($url, 0, 1) === '/') { @@ -585,6 +591,17 @@ class SimpleSAML_Utilities { throw new SimpleSAML_Error_Exception('Redirect to invalid URL: ' . $url); } + /* Validates that URL host is among those allowed. */ + if ($allowed_redirect_hosts != NULL) { + preg_match('@^https?://([^/]+)@i', $url, $matches); + $hostname = $matches[1]; + + /* Throw exception for redirect to untrusted site */ + if(!in_array($hostname, $allowed_redirect_hosts)) { + throw new SimpleSAML_Error_Exception('Redirect not to allowed redirect host: ' . $url); + } + } + /* Determine which prefix we should put before the first * parameter. */ @@ -667,6 +684,26 @@ class SimpleSAML_Utilities { exit; } + /* + * This function validates untrusted url has hostname against + * config option 'redirect.trustedsites'. + * + * If option not set or hostname present among trusted sites, + * peforms redirect via function redirect above. + * + * If site is not trusted, an exception will be thrown. + * + * See function redirect for details on url, parameters and return. + */ + public static function redirectUntrustedURL($url, $parameters = array()) { + $redirectTrustedSites = SimpleSAML_Configuration::getInstance()->getArray('redirect.trustedsites', NULL); + try { + self::redirect($url, $parameters, $redirectTrustedSites); + } + catch (SimpleSAML_Error_Exception $e) { + throw new SimpleSAML_Error_Exception('Site not in redirect.trusted sites: ' . $url); + } + } /** * This function transposes a two-dimensional array, so that