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.