Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.



I'm reading this, and I'm just dumbstruck that, in over 15 years of development, that this is actually a recommended solution to PHP security. This isn't a knock against your configuration; I'm sure it's (probably) fine for your usage, but whatever happened to properly sanitizing and sandboxing uploaded content?

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.


If you're writing the app, you're absolutely right. Apps should be written in such a way that everything is properly sanitized and sandboxed.

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.


Okay, yes, a considerable amount of blame lies with apps and frameworks that have security holes. But here's the thing: make the runtime secure /by default/, so that -- if you really want to run Wordpress, you have to uncomment the PHP configuration labeled "this is extremely hazardous and will likely result in your machine getting compromised" and I think you'll find those third-party apps and frameworks cleaning up their act in a hurry.


Many of those features are completely harmless if used correctly. By itself, exec() doesn't cause your machine to be compromised. It is a valuable function that every scripting language should have. (Would you like Python to disable os.exec* by default just to make websites more secure?) Besides, incompetent developers will always find a way to make vulnerable apps using even the most benign features. The only way to make a runtime secure against fools is to allow nothing.

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.


"Would you like Python to disable os.exec* by default just to make websites more secure?"

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.


IMO, the plethora of poorly written articles and tutorials is the ultimate culprit here. You'll find few go into detail regarding file extensions vs. mime types, file permissions, file ownership, or anything under the hood. When a beginner is left to their own devices with some sample code that doesn't mention the implications of not taking security seriously, we're left with swiss cheese implementations.

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.


> 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.


A couple of days ago a tutorial "PHP MD5 Secure login script" showed up on Hacker News:

http://news.ycombinator.com/item?id=3084834

The article has later been removed from the site.


Same idea, but for nginx:

    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;
            }
    }


You've missed a few file types -- I see that you've got "phtml", but a lot of web servers will also parse files with stupid extensions including:

    .php3 .php4 .php5
    .ph3  .ph4
    .php3p
    .pht


"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.


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.


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.


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.


No, just because a site allows users to upload files doesn't mean they can replace the top-level index.php, for if that were true then whoever setup that server needs to look for job opportunities in the circus.

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 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?"

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.


Yes, there is a way by rewriting (not redirect) the url.

    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.


Yes, using mod_rewrite is what I've been doing for years. My question was on the second situation - how do you let Apache execute the controller directly?


I would prevent uploads of arbitrary PHP files. If they are not parsed by the PHP interpreter, they might be delivered as PHP source code, like an ordinary text file. This could be used to be included into another vulnerable website. So your website is part of an attack, the malicious code is on your server, but remotely included and executed on another machine.

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.


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

As an alternative, you can simply gzip all uploads automatically. You will save hard drive space and prevent any funny business.


Ideally though, you should make sure that anything not part of your system, doesn't get passed through PHP. This will take away this common security risk, as well as limit the overhead required to serve those files.


> Ideally though, you should make sure that anything not part of your system, doesn't get passed through PHP.

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.


Just a thought: can we find some input, which will turn into a malicious script, when compressed or encoded by an algorithm and saved as a file on the server? The encoded file could contain something like <?php ... ?>. If the server could be tricked into parsing the encoded file for PHP, we are in trouble. I think about uploading an additional .htaccess, which will contain a directive to send .zip files to the PHP parser - of course this should never be possible.

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.


> Just a thought: can we find some input, which will turn into a malicious script, when compressed or encoded by an algorithm and saved as a file on the server?

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.


what i do is to serve user facing php files as .html and keep backend includes as .php

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.


But this usage of hidden php can sometimes be detected. If expose_php is not Off, you can see the X-Powered-by HTTP header and the PHP version, if the requested page was a PHP ressource.

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.


I think there probably is some security in changing or omitting file extensions in your URIs. It seems that an awful lot of exploited sites are attacked opportunistically - you find an exploit, find a suitable search term, and google up some victims. If you can move yourself out of matching those general results, you gain the (small) benefit of not being hit by all those automated scan/'sploit scripts.

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.


I couldn't care the slightest about exposing php. Only safe bet is to assume your public facing services will be compromised at one point.

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.




Consider applying for YC's Winter 2027 batch! Applications are open till November 2.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: