Skip to content

[5.4] Prevent PHP file extensions uploads by default unless explicitly allowed - #20392

Merged
taylorotwell merged 5 commits into
laravel:5.4from
laurencei:blockphp
Aug 2, 2017
Merged

taylorotwell merged 5 commits into
laravel:5.4from
laurencei:blockphp

Conversation

@laurencei

@laurencei laurencei commented Aug 2, 2017 •

Copy link
Copy Markdown
Contributor

This is an attempt to fix #20390, #18120 and #18299

Basically during MIME validations we now block any file that ends in .php unless you have explicitly allowed a php MIME. This solves the image spoofing issues, where you can make a php file pass the image validation MIME.

Seems like a sensible default, and if someone is brave enough to want to allow a .php file they still can by explicitly setting it. It doesnt solve general malicious uploads (like a bad pdf file or something) - but I think that .php extensions are a special case, since it can result in remote execution on the server.

Should be backwards compatible - on the basis it is unlikely anyone wanted to allow a .php file in the first place?

Before:
'picture' => 'required|image

  • #c5f015 fake.php could pass in some situations

'picture' => 'required|mimes:gif

  • #c5f015 fake.php could pass in some situations

'picture' => 'required|mimes:gif,php

  • #c5f015 fake.php will pass

After:
'picture' => 'required|image

  • #f03c15 fake.php can no longer pass at all

'picture' => 'required|mimes:gif

  • #f03c15 fake.php can no longer pass at all

'picture' => 'required|mimes:gif,php

  • #c5f015 fake.php will pass

Note:

'picture' => 'required|file

  • #c5f015 fake.php will still pass before/after.

/**
* Check if we have explicity allowed a PHP upload, and check accordingly.
*
* @param string $filename

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

phpdoc is not correct

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks - fixed now.

@Numline1

Numline1 commented Aug 2, 2017

Copy link
Copy Markdown

@laurencei this seems great, however, what about edge cases where other than .php files are processed as PHP? Legacy systems with .php5 extensions come to mind.

@laurencei laurencei changed the title [5.4] Prevent Php file extensions by default unless explicitly alowed [5.4] Prevent PHP file extensions uploads by default unless explicitly allowed Aug 2, 2017
@laurencei

laurencei commented Aug 2, 2017 •

Copy link
Copy Markdown
Contributor Author

If they are that legacy then they can probably no longer run Laravel? 😄

I could add it - but I'm not sure. I'll see if Taylor likes this concept at all, and he can decide.

@taylorotwell

Copy link
Copy Markdown
Member

I'm not sure I really see this as a framework issue if someone is calling system($file)?

@laurencei

laurencei commented Aug 2, 2017 •

Copy link
Copy Markdown
Contributor Author

@taylorotwell - I'll send you an email with how the exploit could work.

edit: email sent to taylor@laravel.com

@taylorotwell
taylorotwell merged commit bbf5b67 into laravel:5.4 Aug 2, 2017
@barryvdh

barryvdh commented Aug 4, 2017

Copy link
Copy Markdown
Contributor

Would it be appropriate to include a default .htaccess file for the public storage folder, which defines a SetHandler to avoid PHP execution for all files in that folder (with Apache), like Drupal? https://www.drupal.org/node/652002

@barryvdh

barryvdh commented Aug 4, 2017

Copy link
Copy Markdown
Contributor

Are you going to backport this to 5.1/5.2/5.3?

@oddergaard

Copy link
Copy Markdown
Contributor

This is the "security vulnerability" I reported on e-mail about 1,5 month ago Taylor, where we couldn't come up with a good way to fix it.
Happy to see @laurencei was able to find a nice clean fix. - Thanks. 👍

I'll probably still instruct my developers to always write another explicit in_array() check of the extension before manipulating files. We are working with some semi-big clients and like to be verbose rather than sorry, for the exact reason of this fix.

This still applies to lots of other formats and I've always found the inspection of byte patterns in the file uploaded problematic. I can understand trying to guess the real file type and not rely on extensions, as Linux and Mac etc. kan easily have files that the user uses normally without an extension if they messed up the naming. Now we are basically building a blacklist of file extensions, but regarding security we should really be whitelisting.

What if we made a list of "known" extensions of the different mimes, and threw and exception on getClientOriginalExtension() if it didn't match any of the ones the user tried to filter by? Possibly even make an getClientOriginalReallyUnsafeExtensionBeSureAboutWhatYouAreDoing() function for the original functionality. (Semi serious about the naming).

@laurencei

laurencei commented Aug 4, 2017 •

Copy link
Copy Markdown
Contributor Author

A general approach could be that the file extension needs to match the expected MIME. i.e. if you upload a file that has a MIME of pdf - then we only accept an extension of .pdf. If it has another other extension - it is rejected.

Same goes in reverse. If you upload a .pdf - then the MIME should match pdf - otherwise something is wrong. Therefore is someone uploads a file that has a MIME of xls but an extension of .exe - it would be automatically rejected. You can use the standard PHP MIME/Extension list to achieve this - would probably capture most situations?

So its a double level protection in addition to what exists now. We dont rely on the MIME or the extension - we rely on both, and force both to match. Could be called strictMimeValidation or something as a rule.

@laurencei

laurencei commented Aug 4, 2017 •

Copy link
Copy Markdown
Contributor Author

Are you going to backport this to 5.1/5.2/5.3?

@barryvdh - I had a quick look. The code would be the same, but it would need to be another PR, as the files are different (they were refactored for 5.4 I think).

I'm not able to do it right now as I'm about to go out for a while - so if someone else wants to do it? (And if Taylor agrees to the backport obviously).

@barryvdh

barryvdh commented Aug 4, 2017

Copy link
Copy Markdown
Contributor

I think you should probably always want that, unless you explicitly disable it. Never allowing php is a start, but perhaps there are other scripts that could be configured to execute. And not just images, but what if someone modifies a pdf to pass mime type?

@laurencei

laurencei commented Aug 4, 2017 •

Copy link
Copy Markdown
Contributor Author

Never allowing php is a start, but perhaps there are other scripts that could be configured to execute. And not just images, but what if someone modifies a pdf to pass mime type?

If the webserver is correctly setup - then only a .php extension is parsed for PHP. So even if I upload a php file with a .gif extension - it shouldnt be possible to run it as the web-user - because the file is never run through the PHP system.

That's why we explicitly block .php for now. It's realistically the only extension that can hurt most webservers.

@barryvdh

barryvdh commented Aug 4, 2017

Copy link
Copy Markdown
Contributor

If the webserver is correctly setup - then only a .php extension is parsed for PHP. So even if I upload a php file as a .gif - it shouldnt be possible to run it as the web-user.

Yeah I was thinking about python (.py) etc, but I guess you're right that it's mostly just PHP we need to worry about, with default configs.

Doesn't Symfony have the same problem with their Mime validation, or do they fix it differently? Can't we use that/

@laurencei

laurencei commented Aug 4, 2017 •

Copy link
Copy Markdown
Contributor Author

Doesn't Symfony have the same problem with their Mime validation, or do they fix it differently?

AFAIK - they suffer the same issue - as the problem is with MIME's themselves - not Symfony or PHP. But this is starting to get beyond my knowledge on the topic - so take that with a grain of salt... maybe someone else knows more they can join in...

@Numline1

Numline1 commented Aug 4, 2017 •

Copy link
Copy Markdown

@barryvdh most Nginx configurations only check the .php extension and then pass the script to an fpm instance. Apache probably checks mime and extension when using mod_php.

This makes me wonder, as I mentioned in the original thread: Wouldn't it be smarter to leave the image rule as it was and instead add better validation for mimes? I'm not sure if PHP's mime handling allows this, so maybe adding an "extension" validation rule would be a nice thing.

"image" rule could then validate mimes and extensions which would were whitelisted, instead of blacklisting .php.

@laurencei

Copy link
Copy Markdown
Contributor Author

Ok - backports done.

@vlakoff

vlakoff commented Aug 5, 2017

Copy link
Copy Markdown
Contributor

You should also block .PhP (case insensitive, for windows boxes), .php5, .php7, etc.

Ideally it could be /^php\d*$/i

@laurencei

Copy link
Copy Markdown
Contributor Author

@vlakoff - we already do a case insensitive check with strtolower()

@vlakoff

vlakoff commented Aug 5, 2017

Copy link
Copy Markdown
Contributor

Not in the in_array part. I haven't examined further if this is fine or not.

@laurencei

laurencei commented Aug 5, 2017 •

Copy link
Copy Markdown
Contributor Author

Yes - because the array part is our definition for a mime allowing php - not the users.

And a correctly configured server should only run .php as an extension. .php7 etc shouldnt be allowed to run anyway. I just checked - and that is how it is on nginx.

@Numline1

Numline1 commented Aug 5, 2017

Copy link
Copy Markdown

Yup. Some shared hostings actually run .php7 and .php5 (or other versions for whatever reason), but I think that's a borderline nonexistent minority.

I would however like to hear about what people thing about my last comment, considering whitelisting instead of blacklisting (blacklisting seems like a tempfix).

@taylorotwell if you'd be okay using mime+extension for validation, I'd be okay with creating the PR, the only issue that comes in mind is people uploading images with non-standard or empty extensions.

@laurencei

Copy link
Copy Markdown
Contributor Author

@Numline1 - I was thinking of maybe making mime "strict" in 5.5 - and have like softMime validation rule or something that isnt as stict as a fallback...

@Numline1

Numline1 commented Aug 5, 2017

Copy link
Copy Markdown

Not sure if I mentioned this before, but perhaps making an "extension" rule, which then could be used separately or aliased in "image" rule would be nice, although I'm not sure if combining rules is a good idea (how would "image" with another separate "extension" rule behave in form request if "extension" rule was already used in "image" in the framework code?).

salmanarshad321 pushed a commit to salmanarshad321/framework that referenced this pull request Sep 23, 2026
…y allowed (laravel#20392)

* inital

* style fix

* style fix

* fix block

* Update ValidatesAttributes.php
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants