diff --git a/imagecache.module b/imagecache.module index 4f31428..a22eade 100644 --- a/imagecache.module +++ b/imagecache.module @@ -45,6 +45,11 @@ define('IMAGECACHE_STORAGE_DEFAULT', 1); */ define('IMAGECACHE_STORAGE_OVERRIDE', 2); +/** + * The name of the query parameter for image derivative tokens. + */ +define('IMAGECACHE_DERIVATIVE_TOKEN', 'itok'); + /********************************************************************************************* * Drupal Hooks *********************************************************************************************/ @@ -342,7 +347,7 @@ function imagecache_action_definition($action) { */ function imagecache_create_url($presetname, $filepath, $bypass_browser_cache = FALSE, $absolute = TRUE) { $args = array( - 'query' => empty($bypass_browser_cache) ? NULL : time(), + 'query' => empty($bypass_browser_cache) ? array() : array(time() => ''), // Little hack to avoid having language_url_rewrite() prefix the path with the // language code, but preserve the domain rewriting. 'language' => (object) array('language' => '', 'domain' => $GLOBALS['language']->domain), @@ -350,7 +355,8 @@ function imagecache_create_url($presetname, $filepath, $bypass_browser_cache = F $file_directory = file_directory_path(); // Determine the path of the derivative inside the files directory. - $derivative_path = 'imagecache/'. $presetname .'/'. _imagecache_strip_file_directory($filepath); + $imagepath = _imagecache_strip_file_directory($filepath); + $derivative_path = 'imagecache/'. $presetname .'/'. $imagepath; // Then construct a full path and see if anyone wants to alter it. $altered_path = $old_path = $file_directory .'/'. $derivative_path; @@ -361,6 +367,10 @@ function imagecache_create_url($presetname, $filepath, $bypass_browser_cache = F // ...but use url() so our $bypass_browser_cache parameter is honored. return url($altered_path, $args); } + // The token query is added even if the 'image_allow_insecure_derivatives' + // variable is TRUE, so that the emitted links remain valid if it is changed + // back to the default FALSE. + $args['query'][IMAGECACHE_DERIVATIVE_TOKEN] = imagecache_style_path_token($presetname, $imagepath); // It was unchanged so use the download method's prefix. $prefix = array( @@ -373,6 +383,29 @@ function imagecache_create_url($presetname, $filepath, $bypass_browser_cache = F } /** + * 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 $presetname + * The name of the image style preset. + * @param $path + * The path of the image. + * + * @return + * An eight-character token which can be used to protect image style + * derivatives against denial-of-service attacks. + */ +function imagecache_style_path_token($presetname, $path) { + // Drupal 6 may not yet have drupal_get_hash_salt(). + global $db_url; + $salt = hash('sha256', serialize($db_url)); + // Return the first eight characters. + return substr(sha1($presetname . ':' . $path . drupal_get_private_key() . $salt), 0, 8); +} + +/** * Return a file system location that points to the location of a derivative * of the original image at @p $path, transformed with the given @p $preset. * Keep in mind that the image might not yet exist and won't be created. @@ -435,9 +468,16 @@ function imagecache_cache_private() { * ImageCache generate images but not send them to a browser. */ function _imagecache_cache($presetname, $path) { - if (!$preset = imagecache_preset_by_name($presetname)) { - // Send a 404 if we don't know of a preset. - header("HTTP/1.0 404 Not Found"); + $preset = imagecache_preset_by_name($presetname); + + $valid = !empty($preset); + if (!variable_get('image_allow_insecure_derivatives', FALSE)) { + $valid = $valid && isset($_GET[IMAGECACHE_DERIVATIVE_TOKEN]) && $_GET[IMAGECACHE_DERIVATIVE_TOKEN] === imagecache_style_path_token($preset['presetname'], $path); + } + + if (!$valid) { + // Send a 403 if the presented token isn't invalid. + header('HTTP/1.0 403 Forbidden'); exit; }