<If> condition with HTTP_HOST in htaccess breaks PHP - php

I'd like to harden my shared hosting webspace to prevent it from rendering PHP with faked filenames like foo.php.wrong as suggested in the TYPO3 Security Guidelines. For details see also https://stackoverflow.com/a/61760273/3946047
Since I don't have access to the Apache configuration I'd like to add the configuration to my .htaccess file. But to make the .htaccess file work on both, my local development environment and in production, I need to wrap the code with a <If> condition to distinguish between production and all other environments.
But as soon as I add the condition, PHP rendering fails for all PHP files. Instead, the source code is shown. To verify it's not a problem of my huge .htaccess file, I created a new subdomain with an empty directory and put this into the .htaccess file:
<IfModule mod_mime.c>
RemoveType .html .htm
<FilesMatch ".+\.html?$">
AddType text/html .html
AddType text/html .htm
</FilesMatch>
RemoveType .svg .svgz
<FilesMatch ".+\.svgz?$">
AddType image/svg+xml .svg
AddType image/svg+xml .svgz
</FilesMatch>
#<If "%{HTTP_HOST} =~ /^(.+)example\.com$/">
RemoveType .php
<FilesMatch ".+\.php$">
AddType application/x-httpd-php73 .php
SetHandler application/x-httpd-php73
</FilesMatch>
#</If>
</IfModule>
This works as long as the condition is commented out. When I activate the condition, even valid PHP files like index.php only show the source code instead of being rendered.
The special type application/x-httpd-php73 is needed because my provider offers several PHP versions.
So my problem is not the configuration itself but the condition. Any ideas how I can fix this?

#<If "%{HTTP_HOST} =~ /^(.+)example\.com$/">
RemoveType .php
<FilesMatch ".+\.php$">
AddType application/x-httpd-php73 .php
SetHandler application/x-httpd-php73
</FilesMatch>
#</If>
This "fails" because the inner <FilesMatch> section (inside the <If> section), that re-enables the PHP handler is never actually processed. This has to do with the order in which sections are merged.
Surrounding the directives in an <If> block changes the order of processing. The contents of the <If> section are merged very late, after <Files> (and <FilesMatch>) sections are merged. So, it seems that at the time the <If> section is processed, it is too late for any child <Files> (or <FilesMatch>) containers to be processed (since any <Files> containers - that are to be processed - have already been processed). Although this is admittedly very counter-intuitive, and there does not appear to be any "warning" of this, as far as I can see, in the Apache docs.
This can be demonstrated with a simple example (which applies to all requests and all files):
<If true>
SetEnv IF_OUTER 1
<Files *>
SetEnv IF_INNER_FILES 1
</Files>
</If>
Without the <If> wrapper, both the IF_OUTER and IF_INNER_FILES environment vars are set. However, with the <If> wrapper (causing the block to be merged late), the IF_INNER_FILES env var is not set. It doesn't matter what directives are used: mod_setenvif, mod_rewrite, etc. the inner <Files> block within the <If> section is never processed.
However, you could use an alternative method using mod_rewrite to block malicious requests of the form foo.php.wrong. For example:
RewriteEngine on
RewriteCond %{HTTP_HOST} example\.com$
RewriteRule \.php\. - [R=404]
Any request to example.com (strictly speaking any hostname that ends with example.com) that contains .php. in the URL-path will simply return a 404.
However, you may need to place this in a different part of your .htaccess file. Preferably near the top. You don't need to repeat the RewriteEngine directive if it's already present.
There may also be other ways you can identify your development server (or rather, the "live server"). eg. You could Define a variable in your development server config and check for the absence of this using <IfDefine> in .htaccess instead.
For example, in your development server config:
Define DEVELOPMENT_SERVER
Then in .htaccess, check that this is not defined in order to identity the live server:
<IfDefine !DEVELOPMENT_SERVER>
# Processed only on live server...
</IfDefine>

Related

About FilesMatch in htaccess

I use "FilesMatch" in my .htaccess file, to get "friendly" links. So, I have a file, called rss (no extension), that has some php-code. In .htaccess I have:
<FilesMatch "^rss$">
ForceType application/x-httpd-php
</FilesMatch>
Now, the hosting company moved the sites to another hosting and the rss file is not executed as php file.
Can you help me to find the problem?
I think it is in php config. But I don't know what is the setting I need to change.
Thanks.
Not really sure if this matters, but the Apache documentation seems to say that it needs to be in a <Files> container. Try using that instead:
<Files "rss">
ForceType application/x-httpd-php
</Files>

php downloaded instead of viewing

I am a beginner of web development and just started to learn about a month
intro:
I recently come across SQL injection and decided to look it up. After summarizing it, I tried to upgrade my cpanel php version from 5.3 to 5.4 due to PDO support. So I was messing around with .htaccess file and after I uploaded my new .htaccess file I can no longer view my php page again. I don't remember what codes did I put.....
Question: What should I do to undo my stupidity?
Tried solution:
I emptied my .htaccess file and still the same result
I loaded up codes in .htaccess file with
LoadModule php5_module modules/libphp5.so
AddHandler php5-script php
AddType application/x-httpd-php php
(from Browser ask me to download php file)
and still the same result
Somewhere I have read to change the php file name will undo the damage, It works. But I like my original php file name, so I tried rename back to the original file name and the problem happens again
Sorry for my bad English practice
Update:
I was browsing my website again, and somehow got into an error saying 500 - Internal Server Error, so I decided to delete all my files in public_html file. Then,I re-upload all my web file again and suddenly it all worked. Did not download php file again.
Thanks for everyone who spends time on looking at my problem. I apologize to all the contributor.
The file name on both regist and register have a extension name of .php
You're being asked to download the file because the server no longer recognizes the file as something it needs to process.
I'm not sure why you would need all the PHP declarations in your .htaccess file (especially LoadModule). Normally those are stored in config files (files like /etc/httpd/conf.d/php.conf). Here's what the default on my box looks like (this is for Apache so YMMV)
# PHP is an HTML-embedded scripting language which attempts to make it
# easy for developers to write dynamically generated webpages.
#
#<IfModule prefork.c>
<IfModule !worker.c>
LoadModule php5_module modules/libphp5.so
</IfModule>
<IfModule worker.c>
LoadModule php5_module modules/libphp5-zts.so
</IfModule>
#
# Cause the PHP interpreter to handle files with a .php extension.
#
#AddHandler php5-script .php
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
AddType text/html .php
#
# Add index.php to the list of files that will be served as directory
# indexes.
#
DirectoryIndex index.php
#
# Uncomment the following line to allow PHP to pretty-print .phps
# files as PHP source code:
#
#AddType application/x-httpd-php-source .phps

php file content is displayed insted of parsed

This must be a simple errror...
I have certain php files in a Drupal site. I can execute them from inside Drupal, but when I need to execute some of them when I submit a form, I get "You don't have permission...". I added a .htaccess file in teh specific directory with this content
<FilesMatch "test\.php$">
Satisfy Any
Allow from all
</FilesMatch>
Now I am able to access the file, but the browser display the content instead of parsing it.
What am I doing wrong?
Seems like php is not enabled in that directory. Enable it by AddType
<FilesMatch "test\.php$">
Satisfy Any
Allow from all
AddType text/html .php .phps
</FilesMatch>
Thanks for all relplies.
It turned out that Drupal create a .htacces file in my directory with the following code after one of their security updates:
# Set the catch-all handler to prevent scripts from being executed.
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
<Files *>
# Override the handler again if we're run later in the evaluation list.
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003
Options None
Options +SymLinksifOwnerMatch
</Files>
That was the cause of the problem.

browser downloads php file from apache web server

I have an apache web server. Let's say this server's domain is example.com.
When I access example.com, then the index.php file is correctly displayed in the browser.
However, when I access e.g. example.com/~user, then the index.php file of /home/user/public_html/index.php file is downloaded rather than displayed.
How do I fix this problem? I changed "expose_php = Off" in php.ini, but nothing has changed.
If you are on Debian/Ubuntu take a look at this file /etc/apache2/mods-available/php5.conf
mine looks like this and you can see I had to comment some line to get PHP working in the user dir
<IfModule mod_php5.c>
<FilesMatch "\.ph(p3?|tml)$">
SetHandler application/x-httpd-php
</FilesMatch>
<FilesMatch "\.php$">
SetHandler application/x-httpd-php
</FilesMatch>
<FilesMatch "\.phps$">
SetHandler application/x-httpd-php-source
</FilesMatch>
# To re-enable php in user directories comment the following lines
# (from <IfModule ...> to </IfModule>.) Do NOT set it to On as it
# prevents .htaccess files from disabling it.
#<IfModule mod_userdir.c>
# <Directory /home/*/public_html>
# php_admin_value engine Off
# </Directory>
#</IfModule>
</IfModule>
Please note that after editing the file you would have to restart apache for the modifications to take effect, the command to restart apache on a debian based system is: /etc/init.d/apache2 restart
Hope this saves someone else the headache. I know this question is old, but it still comes up when searching for this problem.
I'm not sure if this works across all installations of apache2, but I am running apache2 on ubuntu and had the problem of my web browser downloading files instead of displaying the correct index file.
The problem lies in the file /etc/apache2/mods-enabled/dir.conf
The default document setting here was overriding what I had set in /etc/apache2/httpd.conf
So just open up /etc/apache2/mods-enabled/dir.conf and change the order of the files listed.
:)
I've had a similar experience - some php files working OK, but others seem to have the raw php code downloaded.
In my case, it was due to the broken files using the short tag format of <? and ?>. This is not recommended, and you may find the default php.ini has this support forced off. With support off, the php code is sent down to the browser as if it was HTML.
If you can't avoid short tags (as in my case - a whole legacy codebase using short tags), then you can set it to be allowed in php.ini:
short_open_tag = On

Disable PHP in directory (including all sub-directories) with .htaccess

I'm making a website which allows people to upload files, html pages, etc... Now I'm having a problem. I have a directory structure like this:
-/USERS
-/DEMO1
-/DEMO2
-/DEMO3
-/etc... (every user has his own direcory here)
-index.php
-control_panel.php
-.htaccess
Now I want to disable PHP, but enable Server-side includes in the direcories and subdirectories inside /USERS
Can this be done (and if so, how)?
I use WAMP server
Try to disable the engine option in your .htaccess file:
php_flag engine off
To disable all access to sub dirs (safest) use:
<Directory full-path-to/USERS>
Order Deny,Allow
Deny from All
</Directory>
If you want to block only PHP files from being served directly, then do:
1 - Make sure you know what file extensions the server recognizes as PHP (and dont' allow people to override in htaccess). One of my servers is set to:
# Example of existing recognized extenstions:
AddType application/x-httpd-php .php .phtml .php3
2 - Based on the extensions add a Regular Expression to FilesMatch (or LocationMatch)
<Directory full-path-to/USERS>
<FilesMatch "(?i)\.(php|php3?|phtml)$">
Order Deny,Allow
Deny from All
</FilesMatch>
</Directory>
Or use Location to match php files (I prefer the above files approach)
<LocationMatch "/USERS/.*(?i)\.(php3?|phtml)$">
Order Deny,Allow
Deny from All
</LocationMatch>
If you're using mod_php, you could put (either in a .htaccess in /USERS or in your httpd.conf for the USERS directory)
RemoveHandler .php
or
RemoveType .php
(depending on whether PHP is enabled using AddHandler or AddType)
PHP files run from another directory will be still able to include files in /USERS (assuming that there is no open_basedir restriction), because this does not go through Apache. If a php file is accessed using apache it will be serverd as plain text.
Edit
Lance Rushing's solution of just denying access to the files is probably better
<Directory /your/directorypath/>
php_admin_value engine Off
</Directory>
This will display the source code instead of executing it:
<VirtualHost *>
ServerName sourcecode.testserver.me
DocumentRoot /var/www/example
AddType text/plain php
</VirtualHost>
I used it once to enable other co-worker to have read access to the source code from the local network (just a quick and dirty alternative).
WARNING !:
As Dan pointed it out sometime ago, this method should never be used in production. Please follow the accepted answer as it blocks any attempt to execute or display php files.
If you want users to share php files (and let others to display the source code), there are better ways to do it, like git, wiki, etc.
This method should be avoided! (you have been warned. Left it here for educational purposes)
None of those answers are working for me (either generating a 500 error or doing nothing). That is probably due to the fact that I'm working on a hosted server where I can't have access to Apache configuration.
But this worked for me :
RewriteRule ^.*\.php$ - [F,L]
This line will generate a 403 Forbidden error for any URL that ends with .php and ends up in this subdirectory.
#Oussama lead me to the right direction here, thanks to him.
If you use php-fpm, the php_admin_value will NOT work and gives an Internal Server Error.
Instead use this in your .htaccess. It disables the parser in that folder and all subfolders:
<FilesMatch ".+\.*$">
SetHandler !
</FilesMatch>
This might be overkill - but be careful doing anything which relies on the extension of PHP files being .php - what if someone comes along later and adds handlers for .php4 or even .html so they're handled by PHP. You might be better off serving files out of those directories from a different instance of Apache or something, which only serves static content.
On production I prefer to redirect the requests to .php files under the directories where PHP processing should be disabled to a home page or to 404 page. This won't reveal any source code (why search engines should index uploaded malicious code?) and will look more friendly for visitors and even for evil hackers trying to exploit the stuff.
Also it can be implemented in mostly in any context - vhost or .htaccess.
Something like this:
<DirectoryMatch "^${docroot}/(image|cache|upload)/">
<FilesMatch "\.php$">
# use one of the redirections
#RedirectMatch temp "(.*)" "http://${servername}/404/"
RedirectMatch temp "(.*)" "http://${servername}"
</FilesMatch>
</DirectoryMatch>
Adjust the directives as you need.
I use in Centos 6.10 for multiple folders in virtual host .conf definitioin file:
<DirectoryMatch ^/var/www/mysite/htdocs/(nophpexecutefolder1|nophpexecutefolder2)>
php_admin_value engine Off
</DirectoryMatch>
However, even though it doesn't parse php code the usual way it still outputs from a .php things such as variable declarations and text when doing echo e.g.
<?php
echo "<strong>PHP CODE EXECUTED!!";
$a=1;
$b=2;
echo $a+$b;
The above produces in web browser?
PHP CODE EXECUTED!!"; $a=1; $b=2; echo $a+$b;
This could potentially expose some code to users which isn't ideal.
Therefore, it's probably best to use the above in combination with the following in .htaccess:
<FilesMatch ".*.(php|php3|php4|php5|php6|php7|php8|phps|pl|py|pyc|pyo|jsp|asp|htm|html|shtml|phtml|sh|cgi)$">
Order Deny,Allow
Deny from all
#IPs to allow access to the above extensions in current folder
# Allow from XXX.XXX.XXX.XXX/32 XXX.XXX.XXX.XXX/32
</FilesMatch>
The above will prevent access to any of the above file extensions but will allow other extensions such as images, css etc. to be accessed the usual way. The error when accessing .php:
Forbidden
You don't have permission to access /nophpexecutefolder1/somefile.php on this server.
<Files *.php>
Order deny,Allow
Deny from all
</Files>

Categories