Issue #1922814 by Berdir, Damien Tournoud, pwolanin, David_Rothstein, Heine, Bèr Kessels: Fixed Denial-of-service vulnerabiility in Image module.
parent
20ab10429c
commit
1a9949d5c6
|
@ -1 +1,2 @@
|
|||
preview_image: core/modules/image/sample.png
|
||||
allow_insecure_derivatives: false
|
||||
|
|
|
@ -37,6 +37,11 @@ define('IMAGE_STORAGE_EDITABLE', IMAGE_STORAGE_NORMAL | IMAGE_STORAGE_OVERRIDE);
|
|||
*/
|
||||
define('IMAGE_STORAGE_MODULE', IMAGE_STORAGE_OVERRIDE | IMAGE_STORAGE_DEFAULT);
|
||||
|
||||
/**
|
||||
* The name of the query parameter for image derivative tokens.
|
||||
*/
|
||||
define('IMAGE_DERIVATIVE_TOKEN', 'itok');
|
||||
|
||||
// Load all Field module hooks for Image.
|
||||
require_once DRUPAL_ROOT . '/core/modules/image/image.field.inc';
|
||||
|
||||
|
@ -587,16 +592,25 @@ function image_style_options($include_empty = TRUE) {
|
|||
* The image style
|
||||
*/
|
||||
function image_style_deliver($style, $scheme) {
|
||||
// Check that the style is defined and the scheme is valid.
|
||||
if (!$style || !file_stream_wrapper_valid_scheme($scheme)) {
|
||||
drupal_exit();
|
||||
}
|
||||
|
||||
$args = func_get_args();
|
||||
array_shift($args);
|
||||
array_shift($args);
|
||||
$target = implode('/', $args);
|
||||
|
||||
// Check that the style is defined, the scheme is valid, and the image
|
||||
// derivative token is valid. (Sites which require image derivatives to be
|
||||
// generated without a token can set the
|
||||
// 'image.settings:allow_insecure_derivatives' configuration to TRUE to bypass
|
||||
// the latter check, but this will increase the site's vulnerability to
|
||||
// denial-of-service attacks.)
|
||||
$valid = !empty($style) && file_stream_wrapper_valid_scheme($scheme);
|
||||
if (!config('image.settings')->get('allow_insecure_derivatives')) {
|
||||
$valid = $valid && isset($_GET[IMAGE_DERIVATIVE_TOKEN]) && $_GET[IMAGE_DERIVATIVE_TOKEN] === image_style_path_token($style->name, $scheme . '://' . $target);
|
||||
}
|
||||
if (!$valid) {
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
$image_uri = $scheme . '://' . $target;
|
||||
$derivative_uri = image_style_path($style->id(), $image_uri);
|
||||
|
||||
|
@ -784,6 +798,10 @@ function image_style_flush($style) {
|
|||
*/
|
||||
function image_style_url($style_name, $path) {
|
||||
$uri = image_style_path($style_name, $path);
|
||||
// The token query is added even if the
|
||||
// 'image.settings:allow_insecure_derivatives' configuration is TRUE, so that
|
||||
// the emitted links remain valid if it is changed back to the default FALSE.
|
||||
$token_query = array(IMAGE_DERIVATIVE_TOKEN => image_style_path_token($style_name, file_stream_wrapper_uri_normalize($path)));
|
||||
|
||||
// If not using clean URLs, the image derivative callback is only available
|
||||
// with the script path. If the file does not exist, use url() to ensure
|
||||
|
@ -791,10 +809,33 @@ function image_style_url($style_name, $path) {
|
|||
// actual file path, this avoids bootstrapping PHP once the files are built.
|
||||
if ($GLOBALS['script_path'] && file_uri_scheme($uri) == 'public' && !file_exists($uri)) {
|
||||
$directory_path = file_stream_wrapper_get_instance_by_uri($uri)->getDirectoryPath();
|
||||
return url($directory_path . '/' . file_uri_target($uri), array('absolute' => TRUE));
|
||||
return url($directory_path . '/' . file_uri_target($uri), array('absolute' => TRUE, 'query' => $token_query));
|
||||
}
|
||||
|
||||
return file_create_url($uri);
|
||||
$file_url = file_create_url($uri);
|
||||
// Append the query string with the token.
|
||||
return $file_url . (strpos($file_url, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($token_query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a token to protect an image style derivative.
|
||||
*
|
||||
* This prevents unauthorized generation of an image style derivative,
|
||||
* which can be costly both in CPU time and disk space.
|
||||
*
|
||||
* @param string $style_name
|
||||
* The name of the image style.
|
||||
* @param string $uri
|
||||
* The URI of the image for this style, for example as returned by
|
||||
* image_style_path().
|
||||
*
|
||||
* @return string
|
||||
* An eight-character token which can be used to protect image style
|
||||
* derivatives against denial-of-service attacks.
|
||||
*/
|
||||
function image_style_path_token($style_name, $uri) {
|
||||
// Return the first eight characters.
|
||||
return substr(drupal_hmac_base64($style_name . ':' . $uri, drupal_get_private_key() . drupal_get_hash_salt()), 0, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -84,9 +84,16 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Test image_style_url().
|
||||
* Tests image_style_url() with a file URL that has an extra slash in it.
|
||||
*/
|
||||
function _testImageStyleUrlAndPath($scheme, $clean_url = TRUE) {
|
||||
function testImageStyleUrlExtraSlash() {
|
||||
$this->_testImageStyleUrlAndPath('public', TRUE, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image_style_url().
|
||||
*/
|
||||
function _testImageStyleUrlAndPath($scheme, $clean_url = TRUE, $extra_slash = FALSE) {
|
||||
$script_path_original = $GLOBALS['script_path'];
|
||||
$GLOBALS['script_path'] = $clean_url ? '' : 'index.php/';
|
||||
|
||||
|
@ -110,13 +117,28 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
$this->assertNotIdentical(FALSE, $original_uri, 'Created the generated image file.');
|
||||
|
||||
// Get the URL of a file that has not been generated and try to create it.
|
||||
$generated_uri = $scheme . '://styles/' . $this->style_name . '/' . $scheme . '/'. drupal_basename($original_uri);
|
||||
$generated_uri = image_style_path($this->style_name, $original_uri);
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$generate_url = image_style_url($this->style_name, $original_uri);
|
||||
|
||||
// Ensure that the tests still pass when the file is generated by accessing
|
||||
// a poorly constructed (but still valid) file URL that has an extra slash
|
||||
// in it.
|
||||
if ($extra_slash) {
|
||||
$modified_uri = str_replace('://', ':///', $original_uri);
|
||||
$this->assertNotEqual($original_uri, $modified_uri, 'An extra slash was added to the generated file URI.');
|
||||
$generate_url = image_style_url($this->style_name, $modified_uri);
|
||||
}
|
||||
|
||||
if ($GLOBALS['script_path']) {
|
||||
$this->assertTrue(strpos($generate_url, $GLOBALS['script_path']) !== FALSE, 'When using non-clean URLS, the system path contains the script name.');
|
||||
}
|
||||
// Add some extra chars to the token.
|
||||
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
|
||||
$this->assertResponse(403, 'Image was inaccessible at the URL wih an invalid token.');
|
||||
// Change the parameter name so the token is missing.
|
||||
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $generate_url));
|
||||
$this->assertResponse(403, 'Image was inaccessible at the URL wih a missing token.');
|
||||
|
||||
// Fetch the URL that generates the file.
|
||||
$this->drupalGet($generate_url);
|
||||
|
@ -162,6 +184,11 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
$this->assertNoRaw( chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10), 'No PNG signature found in the response body.');
|
||||
}
|
||||
}
|
||||
elseif (!$GLOBALS['script_path']) {
|
||||
// Add some extra chars to the token.
|
||||
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
|
||||
$this->assertResponse(200, 'Existing image was accessible at the URL wih an invalid token.');
|
||||
}
|
||||
|
||||
$GLOBALS['script_path'] = $script_path_original;
|
||||
}
|
||||
|
|
|
@ -286,11 +286,16 @@ $settings['update_free_access'] = FALSE;
|
|||
/**
|
||||
* Twig debugging:
|
||||
*
|
||||
* When enabled, you can use the 'dump' function in Twig templates to output
|
||||
* information about variables, and templates are automatically recompiled
|
||||
* whenever the source code changes.
|
||||
* When debugging is enabled:
|
||||
* - The markup of each Twig template is surrounded by HTML comments which
|
||||
* contain theming information such as template file name suggestions.
|
||||
* - The 'dump' function can be used in Twig templates to output information
|
||||
* about template variables.
|
||||
* - Twig templates are automatically recompiled whenever the source code
|
||||
* changes (see twig_auto_reload below).
|
||||
*
|
||||
* @see http://drupal.org/node/1906392
|
||||
* For more information about debugging Twig templates, see
|
||||
* http://drupal.org/node/1906392.
|
||||
*
|
||||
* Not recommended in production environments (Default: FALSE).
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue