Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jlrdw's avatar
Level 75

Image security

After a couple of post on image validation, I experimented with trying to run a script if a php file made it's way through. I sent this email to Taylor:

I have been experimenting with images and sneaking a php file through. In my test I just see if I can run phpinfo();

In an older custom framework when I open image in new tab I get:

Forbidden

You don't have permission to access this resource.

However in laravel 10 it opened and had the phpinfo displayed.

I suggest adding:

    # Restrict php files direct access
    RewriteCond %{THE_REQUEST} ^.+?\ [^?]+\.php[?\ ]
    RewriteRule \.php$ - [F]

This prevents it. Perhaps add more to it, but beyond my knowledge at this time.

I haven't tested Nginx yet, if someone knows how to do the same for Nginx please post.

Note when I added that code above to the bottom of the stock htacces file Laravel ships with it prevented the execution.

Just FYI.

0 likes
2 replies
Snapey's avatar

why you should never use the client supplied filename and extension

You should also validate file mime types

jlrdw's avatar
Level 75

@Snapey I wasn't able to actually upload a php file, I manually copied it to image folder for testing and created a record.

That's when I noticed it would execute whereas in an older customized framework it wouldn't. Also serving the image through an image controller it wouldn't execute. In which I use an image contoller anyway to serve images.

It's after some recent question on secure uploads I wanted to do more research. I studied how Symfony handles them. Basically they do the same as I do.

I don't allow periods. I break up the entire getClientOriginalName to an array and throw out the extension.

ann.jpg

becomes just ann.   // extension is discarded

I assign a unique name such as

ann_1478_127

After verifying mime type is allowed I assign the extension.

ann_1478_127.jpg

I do check content, type, and size.

https://symfonycasts.com/screencast/symfony-uploads/file-naming#play

https://symfonycasts.com/screencast/symfony-uploads/upload-in-form#play

Coincidentally they use a similar technique I use to still have the original name as part of the file.

I am still going to study this even more after watching a couple of videos on how this hack is done. One video is from one of the laracasts instructors who audits websites for safety.

In one video the person was still able to slip a python script through after the file name was made into a UUID.

Please or to participate in this conversation.