Bug 63798 - mod_headers-directives configured via .htaccess are being ignored with Action-invoked CGI scripts
Summary: mod_headers-directives configured via .htaccess are being ignored with Action...
Status: NEW
Alias: None
Product: Apache httpd-2
Classification: Unclassified
Component: mod_actions (show other bugs)
Version: 2.4.38
Hardware: PC Linux
: P2 normal (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
Depends on:
Reported: 2019-10-02 15:37 UTC by Anders Henke
Modified: 2019-10-02 15:37 UTC (History)
0 users


Note You need to log in before you can comment on or make changes to this bug.
Description Anders Henke 2019-10-02 15:37:05 UTC
Overview: The Action directive is a common way to launch CGI scripts based on their MIME content type. Invoking a CGI script via a custom handler functionally disables any mod_headers-directives present in the current directory's .htacess. 

After some research, Apache does take only some .htaccess-directives from the CGI script's directory, but combines headers with any .htaccess-directives from the CGI script interpreter's directory.

Following steps and setup are used to reproduce this issue:

PHP is invoked via configuration in the "global" apache configuration file (e.g. /etc/apache2/httpd.conf):

ScriptAlias "/cgi-bin/" "/usr/lib/cgi-bin/"

Action x-php /cgi-bin/php
AddHandler x-php .php

/usr/lib/cgi-bin/php is an executable PHP-CGI binary. Different websites owned by different users are hosted on the same server, suexec is used to isolate each users processes from each other. This example leaves out suexec, as it does reproduce with and without suexec.

is the DocumentRoot for a Virtualhost and contains at least test.php, test.html and .htaccess.

test.html is static HTML content, 
test.php does successfully execute some PHP code.

.htaccess does add/edit/remove HTTP headers like

<If "%{HTTPS} == 'on'">
Header set Strict-Transport-Security "max-age=31536000"
Header always set Cache-Control: "private, pre-check=0, post-check=0, max-age=0"
Header set X-Load-Indicator "%b %i %D %l" 
Header always unset X-Powered-By

Additionally, it does enable Basic Authentication:

AuthType Basic
AuthName "Access"
AuthUserFile /home/user123/htpasswd
Require valid-user

Actual result:

Invoking /test.html does prompt for username/password, returns the expected content and both server-default and .htaccess-configured headers.

Invoking /test.php does prompt for username/password, returns the expected content, but only has server-default and PHP's own HTTP-headers set. Any header-configurations present in the .htaccess file are being ignored.

Expected result:
Invoking /test.html and /test.php does prompt for username/password, return the expected content and headers provided by server configuration, .htaccess and PHP for /test.php.

Additional information:

Moving the .htaccess configuration to the global configuration's virtualhost context does enable all functionality as expected: /test.php returns both PHP's own headers and the server-configured headers.

Embedding the .htaccess-directives into a "<Directory /home/user123/docroot>"-section behaves the same way than having them in .htaccess.

From this point of view, Apache does behave somehow inconsistent (or at least: hard to understand), as the .htaccess-configured directives all are eligble to be run from .htaccess, but behave differently when being used in a VirtualHost context.

My assumption: shortly before a CGI script is being launched, a range of ("early") .htaccess-directives are being evaluated and applied (e.g. Basic Authentication). When a CGI script is being launched via Action, Apache does perform an internal redirect, which does change the DOCUMENT_ROOT to CONTEXT_DOCUMENT_ROOT and internally requests /cgi-bin/php/test.php instead of /test.php. mod_headers needs to run "late", after the CGI output has been returned; when doing so, Apache notices the changed DOCUMENT_ROOT and checks for .htaccess in the CGI interpreter's directory (/usr/lib/cgi-bin), but no longer the CGI script's directory (/home/user123/docroot).

After the custom Action handler makes Apache handle the CGI-request as an internal redirect, so the request for /test.php becomes being treated as an internal(!) request for /cgi-bin/php/test.php. From Apache's point of view, a totally different .htaccess has to apply: if /usr/lib/cgi-bin/ is enabled via AllowOverride, a present .htacess is actually being parsed.

Aside from setting headers from the PHP script (essentially duplicating the .htaccess-configuration to PHP code), there's also the following workaround -  the user can change the exact mode of CGI execution:
- prepend "#!/usr/lib/cgi-bin/php" to test.php
- chmod u+x test.php
- add "AddHandler cgi-script .php" to .htaccess

Doing so executes test.php as an "ordinary" CGI script, without any internal redirect or changing CONTEXT_DOCUMENT_ROOT/DOCUMENT_ROOT, yet still executing all effective directives from the script directory's .htaccess file (in this case: both Basic Auth and Headers).