Sunday, December 2, 2012

My PHP WTF Of The Day: max_input_vars


I recently moved the installation of a legacy in-house LAMP application from a dated server (hardware failures) to a virtual machine based on backups. To make the transition as smooth as possible, I chose the same software components: centos, mysql5 (the dumps are not compatible with 5.1 and newer thanks to a charset collation bug), php 5.1.6. Because it's an in-house app behind firewalls I don't need to worry about security updates.

Smoke signal testing passed, and so I gave the OK for the users to continue their work.

After weeks of using it, a problem was reported about just one function of the app that would sometimes return a blank screen. It took me hours of debugging (read: echo) to figure out what's going on, digging through some old PHP code (fun!): It appeared that only 1000 post variables arrived on the server. (Well, 1006 actually, but 2 were added by PHP, and that sounded like a PHP-style limitation of 1000.) A quick google lookup revealed that PHP introduced a new feature where it would limit the number of post variables. For safety reasons.

The variable is called "max_input_vars" with a default of 1000. PHP states that this feature was introduced in 5.3.9, but I'm running 5.1.6 and the limit is enforced.

Because the server is for production, it was running with on-screen warnings turned off. PHP says that it "prints a warning and cuts". For me, that's a real WTF. A post request should be processed as all-or-nothing. It should instead refuse the request completely. But for a technology named "personal home page" the priorities are different.

Cutting the post data is as if a browser would enforce a URL length limit of 256 and just cut there, or a database would just truncate a string too long to fit in a field (oh, no, wait, mysql does this without the strict option, and so did earlier versions of postgres ...).

The purpose of this new feature is to harden security... what it did for me is hardening my hate-hate relationship with PHP.

3 comments:

  1. I'm sure that was very irritating. What an unfortunate way to handle the error. :(

    In the future, you might want to try using something like xdebug temporarily on the server to troubleshoot things like this. That way, you won't have to do all those crazy echos! Or even use FireBug for PHP so that you can send the outputs to your console. :)

    On a side note, one thing that just made me go a bit crazy in your article was the statement that your app is in-house and doesn't need security updates. Remember, the likeliness of an attack is minimal. However, security threats don't just come from the Internet. I'm sure you know this, but please be careful about saying security updates aren't needed. We'd hate for someone with less experience to stumble across your blog promoting the lack of security if your software is behind a firewall.

    ReplyDelete
  2. Thanks for your numerous feedback (mostly on reddit). Most concern/disapprove/bashing about my post was about the following topics:

    1) Why on earth would one want to post more than 1000 variables.
    It seems odd at first... is the user forced to type in his all kind of private detail including his shoe size?
    Here's the case: It's a data import workflow with 3 steps. In the 1st step the user pastes csv data from excel into a textarea. In the 2nd step the program presents the rows nicely formatted to ask a couple questions where needed (checkboxes, radio buttons). Most data is in hidden fields. Step 3 then imports this into the db.
    The whole process just happens to be a lot faster when running thousands of row at once.

    2) Using PHP 5.1.6, the last of the PHP 5.1 branch, a release over 6 years old.
    Reason: The application was written over 6 years ago too, and it's end-of-life (no more development, as I wrote, it's legacy). Each PHP release breaks something, disables or removes some functionality. Hence Upgrading PHP means touching code. And that means breaking functionality, having to fix that, and so on. (In Java that's not the case, code written in 2006 still happily compiles and runs.)
    Keeping the same technology that worked well when the app was written seemed to be the better choice.
    It was pointed out that just being behind a firewall, in house, doesn't mean it's impossible to be vulnerable ... it's just less likely. True.
    The cars built in the year 2006 are less secure than the newest models. Still we see them on the streets. Safety vs. cost.

    3) max_input_vars wasn't just introduced for fun, but because of a real vulnerability. See http://www.ocert.org/advisories/ocert-2011-003.html

    4) Not reading error logs. Yes, the warning could have been seen there. Again, legacy app that worked nicely before, nothing was expected. More recent apps have log file watchers http://bucardo.org/wiki/Tail_n_mail to alert about issues.

    The reason for my post, and my message, was that PHP should throw a fatal error. If it had done that, the problem would have shown up on day one, and it could have been addressed quickly. At least there has been agreement for that ;-) apparently a bug report suggested this https://bugs.php.net/bug.php?id=60876 but was closed as "Not a bug" by the PHP founder himself.

    ReplyDelete
  3. This should be cleared here: http://stackoverflow.com/questions/19042734/is-there-a-limit-like-max-input-vars-in-versions-before-5-3-9/19042981?noredirect=1#19042981

    ReplyDelete