Zero-day in jQuery plugin sample code exploited for at least three years
zdnet.com
zdnet.com
The vulnerability is a combination of Apache v.2.3.9's default setting to not read .htaccess files and my mistake of relying on .htaccess to enforce security of the sample PHP upload component.
To give you some context on how this could happen:
- As the project name implies, this started as a client-side jQuery plugin, with a dummy PHP script to echo out the uploaded file
- Over time, I added a couple of sample server-side upload components, including two for Google App Engine (Python + Golang) - which I used for the demo - and one for PHP, which I never used myself in production
- I used the PHP component for local tests with various possible file uploads, including very large files and chunked uploads, which required enabling all file types for upload. My thinking was that allowing all file types for upload is not critical as long as the handling of those files is properly configured.
- Prior to adding the .htaccess file, I mistakenly assumed developers would configure their Apache server themselves so that no PHP scripts would be executed in the uploads folder. It was only added in this commit: https://github.com/blueimp/jQuery-File-Upload/commit/13931c7...
- The Apache servers I tested with always had support for .htaccess enabled, so I never bothered to check that the default Apache configuration since version 2.3.9 actually disabled it
- The original .htaccess configuration didn't even prevent script execution in all Apache configurations and had to be fixed, see: https://github.com/blueimp/jQuery-File-Upload/pull/3381
Looking back, there are a couple of things that I should have done differently:
- Move out the server-side components into separate repositories
- Inform users better about file upload security - see https://github.com/blueimp/jQuery-File-Upload/wiki/Security
- Never assume people actually read information about security
- Never rely on .htaccess for security configurations in Apache
- Make sure that published code is secure in all default configurations
- Never allow all file types for upload by default, even if it is secure in your configuration
- Recommend users to not upload files in the same root as their executable web application
- Always follow security best practices, even if it makes setup for users more difficult
I wanted to make it really simple for users to install a generic and secure file upload service with a great user interface. Unfortunately, security best practices and ease-of-use are often at odds to each other.
Bonus info:
The client-side component had a cross-site scripting vulnerability in the Iframe Transport HTML site back in 2012: https://github.com/blueimp/jQuery-File-Upload/commit/4175032...
The App Engine components had an open redirect vulnerability back in 2015: https://github.com/blueimp/jQuery-File-Upload/commit/f74d2a8...
But everything else can be avoided with a whitelist of acceptable types by default.
To be honest I'm not really that surprised that the vulnerability stayed hidden for so long; many PHP users are hobbyists or come from a more traditional "webmaster" background. This is not to say that there aren't good PHP programmers, just that there is a large group of novices.
> many PHP users are hobbyists or come from a more traditional "webmaster" background
I seem to remember a somewhat related problem with WordPress. The issue there was made worse by the fact the sample code was part of the default install and so available to anyone who knew the URL.
Edit: A quick search shows I was thinking of an XSS attack. It was due to some bad example code in the default theme folder, which is by default publicly accessible even if not used.
https://arstechnica.com/information-technology/2015/05/activ...
And inexperienced webmasters were definitely part of the target group, since I wanted to make is as accessible as possible, including for those users on shared hosting webspace without access to the Apache configuration files.
If the 'hello world' example is hundreds of lines, then I imagine the effort of using this library to be a few days at least.
If the 'hello world' example is just 3 lines, then I can probably integrate this library into my service in 10 minutes.
Making the sample code more robust (handling errors, checking for legacy configs, etc.), makes it longer, which in turn puts off people like me.
I do think that I share at least part of the blame. Enabling all file types by default was not necessary and would have prevented this issue.
Especially since there are so many inexperienced developers using PHP, the defaults should have been secure in every perceivable Webserver configuration.
[1] I assume that like most of the rest of us he was lagging behind the latest and greatest Apache release a bit. So when he was writing/testing this, it probably wouldn't have been an issue.
I think the bigger issue was that the PHP sample code allowed all file types by default - this would not only affect Apache, but any Webserver that had broad rules to execute PHP scripts found in a directory.
Originally I didn't see this as an issue as I trusted developers to securely configure their server to make sure no uploaded files would be executed, which is why the .htaccess security settings were only added later in this commit: https://github.com/blueimp/jQuery-File-Upload/commit/13931c7...
But neither was the documentation informing developers clearly enough about the security implications, nor should I have relied on people actually reading security notices.
For most developers there doesn't seem to be a differentiation between example code on a blog post and actual producation code.
I think in situations like this it's important to take responsibility and provide as much transparency as possible.
For what it's worth, you'll find lots of people disable support for .htaccess for performance reasons.
It's probably less of an issue these days, but if you support .htaccess files, apache reads the .htaccess file for the current directory, and those for every parent directory, and it happens on every single access.
So for example someone accessing http://foo.bar.com/thing/goes/here would cause the webserver to check for and read /thing/goes/.htaccess, /thing/.htaccess and /.htaccess. If you imagine you've done the sensible thing and got your content nicely split out, you can probably see how that can hurt performance. *nix OSs will be smart and cache the content etc. but you're still wasting time on every access making sys calls.
If you put the access rules in the apache config file, everything is set-up at initialisation time and it never has to make further checks.
Originally the PHP example code didn't have a .htaccess file - I trusted developers that they would configure file uploads securely by themselves, e.g. via Apache configuration files.
The reason I added it was to support developers on shared hosting plans without access to the Apache configuration - something which was very common back then.
The general form - never use default-allow - should be used in most situations where set of possible inputs cannot be exhaustively enumerated. In most situations, you cannot enumerate badness[1]. Instead of trying to define valid input by specifying it's inverse, it's far simpler (and safer) to use default-deny and specify valid input directly.
[1] http://www.ranum.com/security/computer_security/editorials/d...
If the server is configured to serve uploaded files securely, it is feasible to allow all file types for upload (e.g. think of Amazon S3 or Google Cloud Storage).
However since there is no way to ensure that the server security settings to securely handle uploaded files are applied, limiting file uploads with a whitelist minimizes the attack vector sufficiently.
- What went wrong
- Why did it go wrong
- What can be done to ensure this doesn't happen again
Everyone makes mistakes once in a while, the key is learning from them.
Well done!
By now I've also updated the project page with
- Security-related releases on top of the main page: https://github.com/blueimp/jQuery-File-Upload#%EF%B8%8F-secu...
- Security guidelines linked in various places on how to securely set up file uploads: https://github.com/blueimp/jQuery-File-Upload/blob/master/SE...
- A list of the fixed vulnerabilities with instructions on how to fix it for the recent critical one: https://github.com/blueimp/jQuery-File-Upload/blob/master/VU...
My recommendation: add a big warning message that there is at least one known security hole and many unknown ones in the server code and it is up to the just to secure their server properly.
Then fix the demo code but leave the warning there.
It's always up to the developers to check the system they build for security problems. They could use the best frameworks in the world and copy thoro reviewed example code. The end result might still have huge security problems.
I already got a helpful pull request for the main README.me that I've updated by now with
- Security-related releases on top of the main page: https://github.com/blueimp/jQuery-File-Upload#%EF%B8%8F-secu...
- Security guidelines linked in various places on how to securely set up file uploads: https://github.com/blueimp/jQuery-File-Upload/blob/master/SE...
- A list of the fixed vulnerabilities with instructions on how to fix it for the recent critical one: https://github.com/blueimp/jQuery-File-Upload/blob/master/VU...
The demo code was fixed as soon as I could confirm the report from Larry Cashdollar.
Please refer to the vulnerability documentation here to see if you are affected: https://github.com/blueimp/jQuery-File-Upload/blob/master/VU...
A better recommendation is to securely configure ImageMagick, or even better: to use a safer image processing library (e.g libvips or imageflow).
I’ve added some mitigating code and recommendations on how to securely configure ImageMagick to jQuery File Upload, please have a look here: https://github.com/blueimp/jQuery-File-Upload/blob/master/VU... https://github.com/blueimp/jQuery-File-Upload/blob/master/SE...
I'm honestly not sure how I'd be more impressed; if you told me this man actually uses this as his real name in his daily life, or by finding out that this is actually his birth name.
His name really is Larry Cashdollar, born and raised. Sometimes I wrote his name as 'Larry $$' though.
Would definitely write another security vulnerability into my code again if I knew that Larry would report it. ;)
Yet more proof that social media will bring the Internet down!
Or is this a PHP server-plugin which if installed on a PHP server makes them insecure? But of course anything you install on server can make it insecure. No?
This is answered in the plug-in author's thorough note, posted a couple hours before you posted this.
The issue is not in the front-end jQuery library, despite the title.
Sounds like the risk from this is not widely known. Probably the correct solution for Apache would have been to detect presence of now-ignored .htaccess files and signal an error.
I think one of the reasons nobody reported this earlier was that people simply assumed that .htaccess support was the default - Larry Cashdollar, the security researcher, also confirmed this: https://news.ycombinator.com/item?id=18271880
Reading blueimp's and larry's comments here I envy their constructivity, open mindedness and professionalism.
But although the title is somewhat click-bait, I still think this counts as a vulnerability in my project, since there is a possible combination of default Apache setting and default project files that is exploitable.
However I wish Apache would have changed their default config in a way that would have signalled an error if an .htaccess file is present but not applied.
Something that HA user fulafel also pointed out here: https://news.ycombinator.com/item?id=18272407
If the upload script just generated a random filename (046359905445.bin), and then stored the mime type, users filename, other metadata, etc. in a database, that would be a much better design.
However the PHP code was written as easy-to-use sample code and I did not want to introduce a database as dependency and keeping the sanitized filename plus extension keeps the meta information intact.
If I had provided better information about how to securely configure the Webserver to allow all file types for upload, using the original - but sanitized - filenames would not be an issue.
Don't you think maybe he's heard this a few times already?
We detached this comment from https://news.ycombinator.com/item?id=18271880 and marked it off-topic.