<If> condition with HTTP_HOST in htaccess breaks PHP

倾然丶 夕夏残阳落幕 提交于 2021-01-28 07:43:16

问题


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?


回答1:


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


来源:https://stackoverflow.com/questions/63972645/if-condition-with-http-host-in-htaccess-breaks-php

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!