
Securing PHP - jamescun
http://www.jamescun.com/2011/10/securing-php/
======
snorkel
What's missing from just about every PHP app and PHP framework is this bit of
Apache config that prevents execution of trojan PHP scripts:

    
    
        <Directory /var/www/website>
         # Only execute /index.php and no other PHP files
         RewriteCond %{REQUEST_FILENAME} -f
         RewriteCond %{REQUEST_FILENAME} .php$
         RewriteCond %{REQUEST_URI} !=/index.php
         RewriteRule ^(.*)$ not_happening [L,F]
        </Directory>
    
        # Remove other file types from PHP execution
        RemoveHandler .phtml .php3
    

This way attackers can upload whatever PHP file they want but they can't
execute it though an Apache request.

What if the attacker names their trojan "index.php"? Doesn't matter, only top-
level index.php of the site can be executed.

~~~
ars
"every PHP app"??

What about apps that don't limit themself to just index.php?

I never understood this obsession with having everything flow through
index.php. Apache already has a method to dispatch requests to the proper
controller AKA file. Why rewrite it again?

This is fictional security. If someone can upload a file, they can also upload
index.php (and from my experience cleaning up a few hacks, that is exactly
what they do).

And if you (correctly) set it so they can't write to the top level directory,
what you really should do is remove the php handler from the directories they
can write to.

~~~
kijin
Having everything go through index.php (the "front controller") makes it a lot
easier to manage common libraries and use PHP's autoloading capability. You
don't have to add a bunch of includes to every script. You don't have to
change every script if you decide to replace one of the common libraries. You
can force every request to go through the same set of checks (such as
authentication). You can have pretty URLs like "/blog/post/123".

The front controller pattern doesn't have much to do with security, though. It
also doesn't work with apps like WordPress.

~~~
jontas
I personally run my applications entirely through index.php but your argument
does not make sense. You can easily include one file at the top of all your
scripts and have that handle things like autoloading, authentication, etc.

Then if you need to make a change you are still only changing it one spot.

~~~
kijin
If you have a front controller, you don't even need to repeat that single
include in any file. Why repeat the same include in 100 different files if you
can avoid it? Also, what if you realize after a while that you need to change
the location of the single include file because you carelessly placed it in a
publicly accessible directory (bad for security) ? Now you have to edit 100
files.

Another reason: What would your templates look like if your app had 100
different entry points? It would seem silly to have 100 template files in
addition to 100 PHP scripts. So the developer is most likely to mix business
logic with templates in the same file(s). Not good.

Of course, if you're a good enough developer to know that you should avoid
problems like this, it doesn't really matter how you organize your site. But I
think that the front controller pattern helps to push developers (especially
novices) toward the right direction.

------
Udo
I see you marked the cURL functions as "dangerous", advising people to disable
curl_exec and curl_multi_exec. This disables all apps that do HTTP requests
(except those that communicate through the socket functions). Is there any
particular reason why you consider cURL problematic?

~~~
jamescun
You are correct, that was a mistake on my behalf. The post has been amended.

~~~
fooyc
Disabling parse_ini_file looks like a mistake too ;)

(It's just a ini file parser; it has nothing to do with PHP settings.)

~~~
jamescun
I typically disabled it because (historically, not 100% sure now) even with
restrictions in place, you can still read PHP.ini and (not now mind) you could
also write to the INI config with the ini functions.

~~~
Joakal
parse_ini_file is highly useful if you want to avoid tumblr's php file
sensitive exposure problem of includes.

~~~
damncabbage
Could you explain this a little more, please?

~~~
Joakal
I'm referring to Tumblr's typo that did not render the PHP script and due to
an include, it was displayed as plain text:
<http://news.ycombinator.com/item?id=2343330>

You can see some discussions within on how to avoid it.

------
elliottcarlson
Also look in to Suhosin[1] from the Hardened-PHP Project

[1] <http://www.hardened-php.net/suhosin/>

~~~
brokentone
I've looked into that, but the last update was 5 years ago. I realize it's a
patch, so it doesn't necessarily correspond to a particular release, but is it
up to date for modern PHP installs?

~~~
elliottcarlson
I'm not sure where you see that - the latest patch is available for 5.3.7 at
<http://www.hardened-php.net/suhosin/download.html>

It's not up to date with 5.3.8 or 5.4 - but they are always slightly behind -
but not 5 years behind.

Edit: Now I see that their news page is really out of date - my bad.

~~~
brokentone
Ahh yes, thank you. I had always noticed the homepage lists the last release
in 2006 and the documentation seems to be about the same and I never got to
the downloads page.

------
static_cast
That's pretty sound advice. However a lot of scripts won't work if you disable
exec and co.

some other random ideas for php-security:

If you have to enable some form of option to exec binaries be aware that
open_basedir is useless now, because the attacker can just start a python
instance and operate under apache user if you are using mod_php

using fastcgi (mod_fcgid or nginx+php-fpm) and restrictive permissions on your
directories should at least protect your other users home directories.

another idea is prevent malicous scripts is to firewall apache and php from
iptables. there is an iptables module for restricting uid and gid ranges to
have access to the outside world. this could at least prevent a trojan dropped
in /tmp to connect to their irc-server. but you can also disallow outgoing
traffic to port 80, this breaks however all the auto-update features of e.g.
wordpress.

A lot of script-kiddie toolkits can also be stopped by not having
gcc,wget,python etc.pp available to the user running php.

if you have to host sensitive data on the same host as the php application
it's wise to use a jail or at least chroot for php, there are some guides to
put a mod_fcgid php into a chroot

and: never ever use the mysql root user for database connectivity!

------
Mandar
If you don't know much about PHP security, the single best step to secure your
installation is to start by using the recommended PHP.ini file for a
production deployment (often called php.ini-production or php.ini-
recommended). This will set sensible defaults you can tweak for your own app.

Also, open_basedir is nice and should be used whenever you can but it doesn't
match a system-wide chroot.

------
DCoder
Sometimes you really need to invoke external commands through exec(). In those
cases, make sure to use escapeshellcmd() and escapeshellarg().

------
brokentone
Those are some good tips, however, the memory and script execution limits
you're setting there will not play well with any sort of CMS or Framework.
Wordpress, Drupal, Symfony all will need an occasional (or perhaps regular)
bigger load. Minimally, maybe you could leave a note that custom settings
should be made for these systems. I can just see hordes of wanna be sysadmins
(like myself) going out and making all the changes you've said and
unnecessarily break WP.

~~~
kijin
You're right about the 8M memory limit, but I don't think there's anything
wrong with the 15 second execution limit. No page should take over 15 seconds
to generate.

~~~
damncabbage
What about upgrade scripts (usually done via the web interface)?

Some flush()s output out to the browser (to show some initial feedback), but
continue to process and send further output as the upgrade continues.

------
eekfuh
First off, under disabled functions, 'eval' needs to be number one.

Secondly, I was kind of disappointed that this wasn't an overview on common
mistakes users do when learning PHP. This feels basically feels like teaching
someone how to enable safe mode, because you aren't teaching someone why they
should escape sql or why they should encode user submitted content, which to
me is much more important. (Though teaching someone to install Suhosin and
disabling debug output is still important in my opinion, so props for that)

~~~
jamescun
It is a valid point, perhaps I should do a followup, showing how these
settings affect PHP and examples of how someone could exploit them.

------
skeptical
It usually get's to my nerve when people jump in the "PHP is inferior language
because I decided so" bandwagon, but this kind of security strategy ind of
gives some base for such criticism.

Disable external execution functions, eval, disable disable, disable. It
appears to me that this is following the approach of disabling everything and
nothing, then start coding without any security concerns, because you 'secured
your PHP'.

I'm not a security obsessed person, but after hearing so much fuss about PHP
lack of security I headed up to milw0rm a few years ago to see what it was all
about. To my surprise (not so much) all the exploits were based on non
sanitized user data.

Why would you use user data without sanitizing it? Why would you protect
yourself against a file the hacker uploaded? (they should not have uploaded it
in the first place) Why would you feed exec, eval, etc with potentially
dangerous content?

If a programmer is stupid enough to do so, disabling such functions won't
prevent him/her from screwing up big time some other way.

My advice: write proper applications with proper standard security in mind.
It's not that hard.

~~~
infinity
The problem of working with unsanitized user input is not specific to PHP,
this has been a problem with other server side scripting languages as well.

Here is an ancient example: NT Web Technology Vulnerabilities, written by
rain.forest.puppy, Phrack Magazine Volume 8, Issue 54 Dec 25th, 1998.

[http://www.phrack.org/issues.html?issue=54&id=8#article](http://www.phrack.org/issues.html?issue=54&id=8#article)

This is one of the oldest articles on SQL injection I know of.

~~~
ars
Man, I so wish I had published or even just posted it on a newsgroup (usenet).
I discovered/thought of SQL injection in 1996 (with ColdFusion code), but my
boss refused to let me disclose it to anyone (I was young then and actually
listened to him).

He was going to do some big conference or something on the subject and drum up
new business, but nothing ever came of it.

------
peterwwillis
Isn't "secure PHP" an oxymoron?

~~~
46Bit
When written properly, a PHP app is probably just as secure as one written in
any other language.

~~~
uriel
Except that building a "properly written PHP app" is way harder than in almost
every other language in widespread use.

The number of pitfalls and traps you can fall into almost surpasses the 'safe'
parts of the language (and its environment).

~~~
FuzzyDunlop
I think the major problem is PHP's 'noob friendliness'. To achieve that it
goes out of its way to mitigate and recover from bad code and thus doesn't
have a decent set of _well known_ good practices to code against.

The huge vulnerability that opens up is that of data validation, and you can
tighten up your server config all you want, but it won't mean shit without any
of that.

Of course, since PHP is such a comparatively simple language, everyone thinks
they're an expert once they know how to open a mysql connection (through the
now deprecated bindings, of course) and code a simple blog script with basic
CRUD functionality.

As a result, there's a 'simple/complicated' dichotomy when it comes to online
documentation and tutorials, where the beginner developer ignores the
complicated (and typically well thought-out) stuff, and goes for what they can
easily copy and paste or get their head around.

Typically none of that code has any sort of validation or sanitisation. Half
of it might go on about `magic_quotes_gpc` and `mysql_real_escape_string` and
other PHP4-tastic curios, and the rest won't even mention that because
checking user input is seemingly only related to db communication.

I feel pretty strongly about it because I've seen people post code snippets
for PHP, trying to be helpful, but the code is _dangerous_. They serve better
as examples of exactly what you shouldn't do.

And the one thing PHP beginners (and intermediates) need is better, simpler
explanations of responsible coding practices, and how it isn't hard to do at
all (it's only tedious); because the sooner they know, the better.

I should write a book or something.

~~~
jasonlotito
I know you aren't saying otherwise, I just wanted to add a bit to your "well
known good practices" part. The fact is, in the professional PHP community,
there are very much well established best practices (granted, not all our
universal), but most are well established. The problem is, the leap from the
beginning community to that professional community isn't natural. This is
mostly a result of php.net catering to the beginner. It opens the doors to
everyone.

Honestly, I feel there are a large number of quality sources for writing good
PHP code. The problem is that isn't not all focused on PHP.

"everyone thinks they're an expert once they know how to open a mysql
connection"

How true.

PHP is deceptively easy. It's akin to C, in that it will allow you to shoot
your own foot if you ask it.

~~~
FuzzyDunlop
Yeah. I've also found that some well-known good practices might be well-known
in an abstract concept but when it comes down to pure implementation it's a
lot more murky.

Like you say though, that leap from beginner to experienced is so large as to
make practical code examples rather scary, and you can't get anywhere if
you're not confident with experimentation.*

The other problem is that there are many ways to skin a cat and one brilliant
solution might be unworkable for another person. But that's just a
characteristic inherent of any creative pursuit.

This thread's inspired me to try making a nice HTML5 presentation or something
that outlines some of these practices as a beginner's aid. Like how Dive Into
HTML5 really helps you learn what you can actually do with the new additions.

*It's surprising how many people won't experiment because they're worried about breaking something or blowing up their computer, and that irrational risk aversion just makes it difficult to learn what you can and can't get away with; and difficult to jump into the unknown.

