最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

php - Randomise upload filenames (or another solution to hide the original image URL from theft?)

programmeradmin1浏览0评论

Our users upload high resolution artwork that we need to protect from theft. The images are resized and watermarked, but it's still trivial for somebody to guess the original URL and gain access to the full resolution image.

I'm trying to figure out a solution, the easiest (I think?) being to randomise the filenames. I can randomise filenames on upload using something like:

function randomize_uploaded_filename( $filename ) {
    // Do things...
    return $filename
}
add_filter( 'sanitize_file_name', 'randomize_uploaded_filename', 10 );

however this will change the filename before wordpress scales the images. No good.

I can also rename the scaled sizes as they are generated so that they each have a unique random filename (used in conjunction with the above this would mean that all image files can have unguessable filenames, right? almost....):

add_filter( 'image_make_intermediate_size', 'rename_intermediates' );
function rename_intermediates( $image ) {
    $info = pathinfo($image);
    $dir = $info['dirname'] . '/';
    $ext = '.' . $info['extension'];
    $name = wp_basename( $image, "$ext" );

    $randomString = '' // random stuff here

    // Build our new image name
    $new_name = $dir . $randomString . $ext;

    // Rename the intermediate size
    $did_it = rename( $image, $new_name );

    // Renaming successful, return new name
    if( $did_it )
        return $new_name;

    return $image;
}

But, if I do this, wordpress doesn't remember the new filenames. So if the original file is uploaded and saved as, say, painting.jpg , and the scaled versions have some randomised filenames like jkn4jk53u567u.jpg , wordpress will still think that the scaled version is called painting-scaled.jpg , etc. Resulting in broken images.

Where am I going wrong? I really appreciate the help :-)

Edit - Here's the second portion in full as per @SallyCJ 's request:

add_filter( 'image_make_intermediate_size', 'rename_intermediates' );
function rename_intermediates( $image ) {
    // Split the $image path into directory/extension/name
    $info = pathinfo($image);
    $dir = $info['dirname'] . '/';
    $ext = '.' . $info['extension'];
    $name = wp_basename( $image, "$ext" );

    // Generate a random alphanumeric string
    $randcharacters = '0123456789abcdefghijklmnopqrstuvwxyz';
    $randcharactersLength = strlen($randcharacters);
    $randomString = '';
    for ($i = 0; $i < 8; $i++) {
        $randomString .= $randcharacters[rand(0, $randcharactersLength - 1)];
    }
    
    // Build our new image name
    $new_name = $dir . $randomString . '-' . $name . $ext;

    // Rename the intermediate size
    $did_it = rename( $image, $new_name );

    // Renaming successful, return new name
    if( $did_it )
        return $new_name;

    return $image;
}

Edit 27th Feb 2021 in response to Sally CJ's answer:

I apologise for my slow reply, it has been a busy few days! Firstly, thank you for your incredibly educational and detailed response! :O

Your fix is fantastic but not quite working fully for me, I'll explain. When I upload picture.jpg it generates everything as you've said, and Wordpress can now correctly see the location of the original AND scaled image (e.g. 768O4Y7q-picture-scaled.jpg). Brilliant! It can't see all the smaller sizes, though. On the Media Library (upload.php) page it thinks the smaller size BFcUGvP8-picture-100x100.jpg image used as the thumbnail is called picture-100x100.jpg. However, in other areas it can see other various renamed smaller sizes sizes just fine...

I've also noticed that when regenerating smaller sizes it adds another 8 random characters to the start of the filename each time (based off the "new" original filename), and it doesn't delete the old smaller sizes during regeneration. It saves new ones alongside the old ones. Ouch! That's a big problem that'll cause exploding disk space use haha.

This is touched upon briefly on the wp_generate_attachment_metadata reference page:

"This function can be used to regenerate thumbnail and intermediate sizes of the image [...] but it does not check or delete intermediate sizes it previously created for the same image."

There are a few other big issues with the image regeneration, too.

At this point the issues that remain are:

1. The new smaller size locations aren't all being updated, leading to some broken smaller size images (could it be custom image sizes causing the issue? Is 100x100 a custom size?)

2. Image regeneration doesn't delete the old generated smaller sizes.

3. Image regeneration seems to re-check the "original" file's filename and draw from that, which reveals the random string at the start that "hides" it. Also, it prepends the original file with another set of random characters, e.g. JGcYJrP9-picture.jpg would become H7liJRPW-JGcYJrP9-picture.jpg, and the new smaller image sizes would be named things like vWCdtas2-JGcYJrP9-picture-100x100.jpg. After another round of regeneration that file would become say, 2jLL72he-H7liJRPW-JGcYJrP9-picture-100x100.jpg, etc.

4. I would like to truncate the uploaded filenames to 50 characters max. I don't think I mentioned this previously, I was going to do it myself with substr($whatever, 0, 50) but I'm having trouble (it works fine for the original and -scaled images but when I try it on the smaller sizes, it doesn't update their new filenames properly any more).

May I trouble you to help for a little longer? - By the way I don't know if this is frowned upon here but as you have been patient with me and your answers have been extremely detailed, do you per chance have a buymeacoffee or similar? You really deserve it!

Frankly, I don't need to put random characters at the start of the scaled versions - I really only need them on the original to ensure nobody can ever guess the URL. So I don't need to get that working perfectly. I do need to get those regeneration bugs ironed out though :( And I'd really love to be able to restrict the filename length.... users write whole novels in their filenames! >_<

Our users upload high resolution artwork that we need to protect from theft. The images are resized and watermarked, but it's still trivial for somebody to guess the original URL and gain access to the full resolution image.

I'm trying to figure out a solution, the easiest (I think?) being to randomise the filenames. I can randomise filenames on upload using something like:

function randomize_uploaded_filename( $filename ) {
    // Do things...
    return $filename
}
add_filter( 'sanitize_file_name', 'randomize_uploaded_filename', 10 );

however this will change the filename before wordpress scales the images. No good.

I can also rename the scaled sizes as they are generated so that they each have a unique random filename (used in conjunction with the above this would mean that all image files can have unguessable filenames, right? almost....):

add_filter( 'image_make_intermediate_size', 'rename_intermediates' );
function rename_intermediates( $image ) {
    $info = pathinfo($image);
    $dir = $info['dirname'] . '/';
    $ext = '.' . $info['extension'];
    $name = wp_basename( $image, "$ext" );

    $randomString = '' // random stuff here

    // Build our new image name
    $new_name = $dir . $randomString . $ext;

    // Rename the intermediate size
    $did_it = rename( $image, $new_name );

    // Renaming successful, return new name
    if( $did_it )
        return $new_name;

    return $image;
}

But, if I do this, wordpress doesn't remember the new filenames. So if the original file is uploaded and saved as, say, painting.jpg , and the scaled versions have some randomised filenames like jkn4jk53u567u.jpg , wordpress will still think that the scaled version is called painting-scaled.jpg , etc. Resulting in broken images.

Where am I going wrong? I really appreciate the help :-)

Edit - Here's the second portion in full as per @SallyCJ 's request:

add_filter( 'image_make_intermediate_size', 'rename_intermediates' );
function rename_intermediates( $image ) {
    // Split the $image path into directory/extension/name
    $info = pathinfo($image);
    $dir = $info['dirname'] . '/';
    $ext = '.' . $info['extension'];
    $name = wp_basename( $image, "$ext" );

    // Generate a random alphanumeric string
    $randcharacters = '0123456789abcdefghijklmnopqrstuvwxyz';
    $randcharactersLength = strlen($randcharacters);
    $randomString = '';
    for ($i = 0; $i < 8; $i++) {
        $randomString .= $randcharacters[rand(0, $randcharactersLength - 1)];
    }
    
    // Build our new image name
    $new_name = $dir . $randomString . '-' . $name . $ext;

    // Rename the intermediate size
    $did_it = rename( $image, $new_name );

    // Renaming successful, return new name
    if( $did_it )
        return $new_name;

    return $image;
}

Edit 27th Feb 2021 in response to Sally CJ's answer:

I apologise for my slow reply, it has been a busy few days! Firstly, thank you for your incredibly educational and detailed response! :O

Your fix is fantastic but not quite working fully for me, I'll explain. When I upload picture.jpg it generates everything as you've said, and Wordpress can now correctly see the location of the original AND scaled image (e.g. 768O4Y7q-picture-scaled.jpg). Brilliant! It can't see all the smaller sizes, though. On the Media Library (upload.php) page it thinks the smaller size BFcUGvP8-picture-100x100.jpg image used as the thumbnail is called picture-100x100.jpg. However, in other areas it can see other various renamed smaller sizes sizes just fine...

I've also noticed that when regenerating smaller sizes it adds another 8 random characters to the start of the filename each time (based off the "new" original filename), and it doesn't delete the old smaller sizes during regeneration. It saves new ones alongside the old ones. Ouch! That's a big problem that'll cause exploding disk space use haha.

This is touched upon briefly on the wp_generate_attachment_metadata reference page:

"This function can be used to regenerate thumbnail and intermediate sizes of the image [...] but it does not check or delete intermediate sizes it previously created for the same image."

There are a few other big issues with the image regeneration, too.

At this point the issues that remain are:

1. The new smaller size locations aren't all being updated, leading to some broken smaller size images (could it be custom image sizes causing the issue? Is 100x100 a custom size?)

2. Image regeneration doesn't delete the old generated smaller sizes.

3. Image regeneration seems to re-check the "original" file's filename and draw from that, which reveals the random string at the start that "hides" it. Also, it prepends the original file with another set of random characters, e.g. JGcYJrP9-picture.jpg would become H7liJRPW-JGcYJrP9-picture.jpg, and the new smaller image sizes would be named things like vWCdtas2-JGcYJrP9-picture-100x100.jpg. After another round of regeneration that file would become say, 2jLL72he-H7liJRPW-JGcYJrP9-picture-100x100.jpg, etc.

4. I would like to truncate the uploaded filenames to 50 characters max. I don't think I mentioned this previously, I was going to do it myself with substr($whatever, 0, 50) but I'm having trouble (it works fine for the original and -scaled images but when I try it on the smaller sizes, it doesn't update their new filenames properly any more).

May I trouble you to help for a little longer? - By the way I don't know if this is frowned upon here but as you have been patient with me and your answers have been extremely detailed, do you per chance have a buymeacoffee or similar? You really deserve it!

Frankly, I don't need to put random characters at the start of the scaled versions - I really only need them on the original to ensure nobody can ever guess the URL. So I don't need to get that working perfectly. I do need to get those regeneration bugs ironed out though :( And I'd really love to be able to restrict the filename length.... users write whole novels in their filenames! >_<

Share Improve this question edited Feb 27, 2021 at 3:31 obinice asked Feb 15, 2021 at 20:57 obiniceobinice 114 bronze badges 11
  • Your code looks good to me, so are you sure that you're getting broken images? What code did you try when displaying the image? Could it be a caching issue (did you try clearing the site and your browser caches)? – Sally CJ Commented Feb 16, 2021 at 0:59
  • @SallyCJ I'm using the normal WP Media library for this whole process. Using the latter method above to rename the scaled sizes, if I upload an image the scaled sizes are generated and named randomly e.g.: 5p1qntwy.jpg 1stykqpwq.jpg ..etc, and the original filename has the randomly generated characters inserted at the start, e.g.: b90a5NUv-IMG_9813.jpg WP gets the original file URL right, but gets the scaled image sizes wrong. It thinks the main scaled image is e.g.: b90a5NUv-IMG_9813-scaled.jpg But, the main scaled image is actually called 5p1qntwy.jpg. – obinice Commented Feb 16, 2021 at 18:53
  • Are you actually using both the first (sanitize_file_name) and second (image_make_intermediate_size) filters? You should use just the second - try removing the first and see if the issue persists? And do you also want to randomize the name of the original image? – Sally CJ Commented Feb 17, 2021 at 9:30
  • First off - I really appreciate your trying to help me out :-) Unfortunately just using the second filter on its own has the same problem. It successfully renames all the scaled images, but Wordpress has no idea what those filenames are - it blindly thinks they're the regular filenames that it would normally generate. I'm guessing I need to figure out how to adjust how Wordpress stores these filenames in its database when it generates them, so it remembers the correct modified filenames for all the sizes? That's currently a bit beyond my knowledge. – obinice Commented Feb 17, 2021 at 15:25
  • Just doing some research - Wordpress stores attachments as posts with the post_type of attachment, and the full URL of the original image is stored in guid. I don't see any information about how it stores the location of the scaled images though, I'm worried that perhaps it doesn't - and perhaps it always relies on extrapolating from the original image URL? If this is the case, I'm truly at a loss as to how I can ensure the URL of the original image is not guessable by the public & thus stealable :-( – obinice Commented Feb 17, 2021 at 15:50
 |  Show 6 more comments

1 Answer 1

Reset to default 1

Where am I going wrong?

Actually, your code is just fine.

Except that I would simply use wp_generate_password() to generate the random string. :)

WordPress will still think that the scaled version is called painting-scaled.jpg , etc. Resulting in broken images.

Yes, that's correct.

Images are stored as posts of the attachment type in the WordPress posts table (in the database), and the details (width, height, full file path, etc.) of the main image and its intermediate sizes (e.g. thumbnail and medium) are stored in a (private) meta named _wp_attachment_metadata — see wp_generate_attachment_metadata() and wp_get_attachment_metadata() for more info, but the meta value basically consists of:

  • 'width' (int) The width of the attachment.
  • 'height' (int) The height of the attachment.
  • 'file' (string) The file path relative to wp-content/uploads.
  • 'sizes' (array) Keys are size slugs, each value is an array containing 'file', 'width', 'height', and 'mime-type'.
  • 'image_meta' (array) Image metadata.

And as for the -scaled version, if available, it will be used with the file item above and there'll be an additional item named original_image added to the above meta where the item's value will be the path to the originally uploaded image file — see wp_get_original_image_path().

So because the image_make_intermediate_size filters are applied both when generating the -scaled version and the intermediate sizes, then the -scaled version also gets renamed by your custom filter (the rename_intermediates() function), but unlike the above sizes item, WordPress no longer updates the file item once it is set after the -scaled version is generated.

Therefore that resulted in the broken image for the -scaled version.

But don't fret, there's a solution.

发布评论

评论列表(0)

  1. 暂无评论