The Linux Page

The HTTP Authentication header is missing / not sent to my CGI script, why?

Puzzle with a missing piece

If you are using Apache2, you may notice that the HTTP_AUTHORIZATION is missing from the list of variables sent to you. This is because Apache2 decides to not send clear passwords (even if base64 encoded) across processes.

In the old days (and the capability is still available), Apache would call processes with command line parameters as the data, instead of having environment variables.

Unfortunately, they decided to keep things that way when they switch to FastCGI. So you do not get the Authorization header passed down to your CGI scripts. Whether you use Node.js (through CGI-Node, for example) or even just a Bash script, that header will always be missing from the list of parameters present in the environment.

The HTTP_AUTHORIZATION is probably the one header that comes up as missing over and over again. However, that happens with other headers such as If-Modified-Since or If-None-Match.

Luckily, you may forcibly add a header when you use mod_rewrite as in:

RewriteRule ^(.*)$ /cgi-bin/my-script.sh [last,passthrough,qsappend,env:HTTP_AUTHORIZATION=%{HTTP:Authorization}]

However, in this case, the header will always appear. It will be empty if it was not defined in the client's request, but that means you can't know whether it was defined or not.

A better way is to use a conditional environment variable:

SetEnvIf Authorization "(.+)" HTTP_AUTHORIZATION=$1

Here we grab the value of Authorization and if not empty, set the HTTP_AUTHORIZATION environment variable. You could further only define the variable if the input says "Bearer ..." as in:

SetEnvIfNoCase Authorization "(Bearer .+)" HTTP_AUTHORIZATION=$1

Note that here I use the SetEnvIfNoCase so the regular expression is checked case-insensitively.

I think that generally the SetEnvIf is better and should be used instead of the RewriteRule.

Note that the RewriteRule will rename the variable to:

REDIRECT_HTTP_AUTHORIZATION

That, however, I can't really explain. This is an internal redirect, I guess. The fact is that in the end, you do not get an HTTP_AUTHORIZATION at all. Only a REDIRECT_HTTP_AUTHORIZATION. Of course, in your CGI implementation, you have a getenv or some similar function so such detail can be hidden from the end user.

Here is an example of such a function I use in my Node.js CGI script:

    getVar(name)
    {
        if(name in process.env)
        {
            return process.env[name]
        }
        if(name === 'HTTP_AUTHORIZATION'
        && 'REDIRECT_HTTP_AUTHORIZATION' in process.env)
        {
            return process.env['REDIRECT_HTTP_AUTHORIZATION']
        }

        return ''
    }

As we can see, if the user calls that function with HTTP_AUTHORIZATION and that variable is not defined in the environment, then I check with REDIRECT_HTTP_AUTHORIZATION instead. Simple.

What about PHP, I also don't see that header?

In PHP, you should use the apache_request_headers() function because it has access to all the headers, at least in most installations. This is because PHP is most often run as a module and not a separate CGI process.

So that means PHP receives the Authentication header directly.

$headers = array_change_key_case(apache_request_headers());
if(empty($headers['authorization']))
{
    bad_authorization(401, "invalid_request", "To log in, please use the \"Basic\" authentication type and your Secret Key and API Key identifiers separated by a colon and base64 encoded.");
    exit(0);
}

// the Authorization value is "<type> <token>"
//
$auth_type_token = explode(" ", $headers["authorization"]);
if(count($auth_type_token) != 2)
{
    bad_authorization(401, "invalid_request", "The Authorization header had more than one space. Try again with only one space between \"Basic\" and token.");
    exit(0);
}

// now check $auth_type_token[0] for the type
// and $auth_type_token[1] for the session ID
...

Remember, if you are just using CGI, you do not have access to the full set of headers.