diff --git a/core/includes/common.inc b/core/includes/common.inc index 0f252ca..ed1245e 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -683,8 +683,9 @@ function drupal_encode_path($path) { */ function drupal_goto($path = '', array $options = array(), $http_response_code = 302) { // A destination in $_GET always overrides the function arguments. - // We do not allow absolute URLs to be passed via $_GET, as this can be an attack vector. - if (isset($_GET['destination']) && !url_is_external($_GET['destination'])) { + // We do not allow absolute URLs to be passed via $_GET, as this can be an attack vector, with the following exception: + // - absolute URLs that point to this site (i.e. same base URL and base path) are allowed + if (isset($_GET['destination']) && (!url_is_external($_GET['destination']) || _external_url_is_local($_GET['destination']))) { $destination = drupal_parse_url($_GET['destination']); $path = $destination['path']; $options['query'] = $destination['query']; @@ -707,6 +708,22 @@ function drupal_goto($path = '', array $options = array(), $http_response_code = } /** + * Helper function for determining if an external URL points to this Drupal installation. + * + * @return + * TRUE if the URL has the same domain and base path + */ +function _external_url_is_local($url) { + $url_parts = parse_url($url); + $base_parts = parse_url($GLOBALS['base_url']); + + // When comparing base path's, we need a trailing slash to make sure a + // partial URL match isn't occuring. Since base_path() always returns with + // a trailing slash, we don't need to add the trailing slash here. + return ($url_parts['host'] == $base_parts['host'] && stripos($url_parts['path'], base_path()) === 0); +} + +/** * Performs an HTTP request. * * This is a flexible and powerful HTTP client implementation. Correctly