Securing PHP
jamescun.com
jamescun.com
<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.
.php3 .php4 .php5
.ph3 .ph4
.php3p
.pht server
{
server_name domainname.com;
root /srv/sites/domainname.com/httpdocs;
index index.php;
access_log /srv/sites/domainname.com/logs/access.log main;
error_log /srv/sites/domainname.com/logs/error.log info;
try_files $uri $uri/ /index.php?$query_string;
location ~ \.php$
{
try_files $uri $uri/ /index.php?$query_string =403;
include /usr/local/nginx/conf/fastcgi_params;
fastcgi_pass phpfpm;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}As an alternative, you can simply gzip all uploads automatically. You will save hard drive space and prevent any funny business.
For you to upload a file to your system it has to pass through PHP. That's where the problem is. Once it's on your system, it can be executed by other means ... those means not necessarily being Apache or NGINX. By encoding the upload, you not only reduce the size, but also render it harmless.
There are some encoder/decoder tools on the web, where it is possible to enter, for example, base64 encoded strings and get these decoded. It is often possible to enter base64 encoded JavaScript plus some closing tags or brackets to break out of the input field. When decoded, the JavaScript will be executed by the browser and we have found a cross-site scripting flaw.
This is not possible with gzip:
http://www.gzip.org/zlib/rfc-gzip.html#file-format
I should mention that on top of this technique, I also rename all files uploaded to a random hash that is stored in a database. That hash is never revealed to the client. When the file is downloaded, the real name is provided from the database as the file name, while the source of the file is read from its true location.
So even if an intruder were to upload a malicious file and somehow managed to bypass the encoding algorithm ... he wouldn't know where to find it. He would have to hack the database on top of everything just to find out where it is.
then just block apache to serve anything .php
this usually get me covered of most backdoors bugs in pear and such. ...not that i do that because of this reason. it was first only for organizing the code tree.
You can also try to get the PHP eastereggs displayed with something like this, on a PHP generated page:
http://example.com/?=PHPE9568F36-D428-11d2-A769-00AA001ACF42
Trying to provoke errors or weird PHP specific behaviour, maybe with PHP error messages displayed, is another way to gather some informations about the script language used by a site. But this is already more aggressive than simply looking for an X-Powered-By: header.
Just rewriting the file extension does not make a site any safer. Omitting file extensions is generally an interesting alternative, because in case you change from .php to .aspx or whatever, all URLs stay the same. Cool URIs don't change.
Yes, it's security through obscurity, and is absolutely not something you should rely on, but it can reduce the number of people giving your wobbly-looking front door a kick.
Of course, if everyone starts doing this, the scanners will switch to more robust methods of testing for your language and server types and versions.
it was a decision mostly done by usability and code clarity (separate presentation scripts from data/model ones)
But one benefit of not having the regular setup, is that even if i'm a victim of an automated attack, it will probably fail because it did not expected to not be serving .php or .phtml files as php. For a skilled targeted attack i'd still be as hopeless as the person hiding php.
had that happen when one box was compromised because of a ssh key bug. but it turned out, the attack was automated and had vectors that worked with linux x86 and several common unixes. i was running irix on an very old box. so i had just some weird log entries instead of a root kit. not that it prevented the paranoid i am to wipe the system as if i had a root kit.
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.
The front controller pattern doesn't have much to do with security, though. It also doesn't work with apps like WordPress.
Then if you need to make a change you are still only changing it one spot.
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.
How? Is there a way to let Apache, for every request to host.domain.com execute /var/www/site/index.php ? I've seen a post somewhere claiming that years ago, and I've tried many times to get it to work or re-find that post but I never managed to do so.
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/index.php
RewriteRule ^/(.*) /index.php/$1
I didn't test it, but it's probably as simple as that.But that's not what I meant. Instead of having index.php look at the url parameters and include the proper controller, just directly execute that controllers .php file. Make a file that you include in each controller that does any prep work you need and that's it, let apache execute the controller directly.
The problem with removing the PHP handler from all other writable directories is the reality of dealing with many 3rd party modules, themes, and apps that have their own upload directories, and sure you can audit all 3rd party code and rewrite it yourself, but you should also assume the code is full of holes anyway and configure the other layers in your stack according to the principle of least privilege.
I mean, I'm trying to imagine another web application language where "attackers can upload whatever (X) file they want" is even a valid case, ever. I mean, imagine a war file being wholesale replaced-- it isn't possible without going out of your way to misconfigure your app server. This, I think, is the base problem with PHP; the default configuration is predicated upon content and code not being segregated at all, and hot redeploy is the norm, without any notification to the admin that it's actually happening.
Unfortunately, a lot of people who use PHP are stuck with third-party apps with questionable security records, such as WordPress. If you want to have anything resembling security in such an environment, you need to resort to band-aid solutions such as disabling functions. It's far from optimal, I admit, but at least it helps reduce damages if (or rather, when) the third-party app gets hacked.
There is also the logistical problem that if the default runtime can't run popular apps such as WordPress, people will simply switch to a broken runtime instead of cleaning up WordPress. This is a particularly big problem in shared hosts who need to make their service compatible with everything or risk losing business. It's been difficult enough to get them to adopt PHP 5.3.
On the other hand, safe_mode, register_globals, magic quotes, etc. can and should be disabled by default. Good news: The PHP team is finally getting around to deprecating them. Also, I use dotdeb's FPM packages on my servers, and dotdeb's default configuration tends to be pretty good.
Yes! Under no circumstances should you ever be spawning new processes from your web application on the fly, ever. You should be using trusted communication to an existing worker thread that is fired up on application start and then never touched except for IPC (think Akka), or trusted communication to an existing process on another machine separate from your web / app server that you can start or stop yourself, separate from the web process.
"Besides, incompetent developers will always find a way to make vulnerable apps using even the most benign features."
Very true; however, you can make it extremely difficult to do so, and you can effectively limit the damage in case of breach. That's what most of the configuration is, from the parent article. Stop someone from being able to write an arbitrary file on the filesystem, by telling them to either store a blob somewhere, or sending data to S3, or whatever, and it will be extremely hard to overwrite existing application code that gets re-interpreted every time it's updated.
"There is also the logistical problem that if the default runtime can't run popular apps such as WordPress, people will simply switch to a broken runtime instead of cleaning up WordPress."
Disagree-- the onus is on Wordpress to keep up with the current version of whatever language they decide upon. When PHP version X disallows re-compilation of scripts on the fly without some sort of notification or server restart, some people and hosts will stay on version X-1, sure, but that's going to be really hard when every distribution starts packaging version X. Wordpress, PHPBB, etc., will need to go through some development pain, but ultimately those packages will wind up being more secure.
The reality is that these kinds of articles shouldn't have to exist. I'm even a little shocked at the Java community, because Red Hat's documentation on "How to Secure JBoss" actually exists, too-- remove jmx-console, remove web-console, and limit access to the http-invoker and jmx-invoker services (and that's it). What Red Hat should be doing is to have a "production" configuration that does all these for you.
This may inherently be due to PHP's age. As an early dynamic language, it could be expected that the earliest shared source code and tutorials would likely contain bugs.
Unfortunately, a lot of recent PHP tutorials published in seemingly up-to-date websites also contain WTF code. Case in point: this tutorial [1] showed up on reddit a couple of days ago.
[1] http://www.anil2u.info/2011/06/object-oriented-programming-i...
Nowadays, I view any PHP code outside of Stack Overflow and Github with the utmost suspicion.
http://news.ycombinator.com/item?id=3084834
The article has later been removed from the site.
Also, if nearly arbitrary PHP files can be uploaded, the chances might be good that it's possible to upload other files as well, like some additional .htaccess files for subdirectories.
(It's just a ini file parser; it has nothing to do with PHP settings.)
You can always detect the contents of php.ini using functions like ini_get. Disabling those isn't recommended, as a lot of applications use them legitimately to read things like memory_limit or safe_mode.
> You could also write to the INI config with the ini functions.
That sounds really unlikely, unless you had php.ini owned by your Apache user or you were running PHP as root. Either one of those possibilities would have left you open to much worse problems.
Perhaps you're thinking of ini_set? That only affects the current request; it doesn't have any long-term effects, nor does it write to config files.
You can see some discussions within on how to avoid it.
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.
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!
Also, open_basedir is nice and should be used whenever you can but it doesn't match a system-wide chroot.
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.
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)
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.
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
This is one of the oldest articles on SQL injection I know of.
My guess is that many don't know what they are so they apply follow 'secure PHP' guides and feel safe, though they are not.
He was going to do some big conference or something on the subject and drum up new business, but nothing ever came of it.
The number of pitfalls and traps you can fall into almost surpasses the 'safe' parts of the language (and its environment).
> Except that building a "properly written PHP app" is way harder than in almost every other language in widespread use.
This makes no sense. PHP doesn't force you to do things the right way, but it doesn't force you to do things the wrong way either. It just doesn't force you either way.
Those of us who know what we're doing can make intelligent decisions ... and those of us who can't, shouldn't be writing in PHP.
To be dismissive of those who 'don't know what they're doing' doesn't necessarily help make those intelligent decisions better known and easier to understand, or why they're the intelligent decisions in the first place.
Yup.
> And everyone starts off at the beginning when they're picking up a new language (or want to start learning).
Yup. PHP is a terrible language to be your first. Its loose nature, which allows an expert to sculpt beautiful code, is a noose upon which to hang oneself as a novice. Before I started on PHP (more than a decade ago), I already knew BASIC, C, and Java. Contrary to popular opinion, PHP is not a starter language, even though it is very easy to start.
Maybe it's like learning to break the rules before you even know what they are?
* although to counter that, you can do some crazy things with it if you know how to. I abstracted Drupal's path finding method (drupal_get_path) into a magic static class. It's probably got a performance hit but it's bloody nice to look at:
Module::module_name('css', 'example.css');
// vs.
drupal_add_css(drupal_get_path('module, 'module_name').'/css/'.$file_name);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.
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.
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.
What planet are you from?
Actually asking for examples sounds like a trolling attempt to me.