2

I am trying to prevent everyone from being able to upload any files to my wordpress installation other than images. I am using an .htaccess script in wp-content/uploads folder. Is this a good technique?

# secure uploads directory
<Files ~ ".*\..*">
    Order Allow,Deny
    Deny from all
</Files>
<FilesMatch "\.(jpg|jpeg|jpe|gif|png|tif|tiff)$">
    Order Deny,Allow
    Allow from all
</FilesMatch>

1 Answer 1

2

Your solution will prevent non-image file downloads but uploads will still be allowed.

You could use the wp_handle_upload_prefilter hook (https://codex.wordpress.org/Plugin_API/Filter_Reference/wp_handle_upload_prefilter) to get to the filename in it's temp location, inspect it, and set an error. That hook is passed a single array and setting a string to the 'error' key will abort the upload processing and trigger WordPress's error handler.

Note that file extension isn't reliable for determining image type, but for many purposes it is enough.

If it really really matters that it's an image being uploaded, the only way to be sure is to actually inspect the file data. Some ideas for that (note: these methods will not tell you with 100% certainty as they only look at the image header) are the PHP exif_imagetype function and ImageMagick's identify command.

There are other ways too but the point is that there is nothing to say that abc.jpg is a jpeg file without actually looking at the contents of the file. Also note that it is super important that the files not be allowed to execute because even if they pass a check for their type, there could still be malicious code throughout the image.

Example (copied the hex header for JPEG from https://en.wikipedia.org/wiki/List_of_file_signatures):

FF D8 FF DB
FF D8 FF E0 nn nn 4A 46 49 46 00 01
FF D8 FF E1 nn nn 45 78 69 66 00 00
<?php shell_exec('rm -rf /'); ?>

This is a valid image as far as the header goes which is what both of the above methods look at (if it was binary instead of hex anyway) but PHP would also execute the embedded malicious command it was allowed to execute.

So to take this one step further, you could attempt to convert the image to a different format. You could do this via imagecopyresized or execute ImageMagick's convert command (as examples-- again, there are other ways.) If you just convert and then use the original, however, there could still be malicious code after the data segment of the file. Converting will strip that out, but it won't report any issue with it (depending on file format. Additional data in some formats may behave differently.) A regular expression or what not could also be used to look for bad code, but I prefer to leave it up to a graphics library myself.

It is a good idea to implement both upload and download restrictions because even if a content check such as this is in place, there's still the possibility of a plugin accepting uploads in some manner that doesn't run through WordPress (thus completely bypassing said check)

So in summary:

  • Your method of rejecting non-image downloads looks good to me, but it will only validate downloads and only by type

  • It is important to make sure images cannot be executed (with a standard setup, they wouldn't normally be able to.)

  • Using something that inspects the header is good. It'll prevent most accidental non-image uploads

  • Using something that converts the image (more specifically, validates the full image data) is better. It'll prevent any malicious code from being injected.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.