php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #68851 PHP and Apache2 interpret duplicate Authentication headers differently
Submitted: 2015-01-18 12:26 UTC Modified: 2015-01-18 13:08 UTC
From: phillip dot berndt at googlemail dot com Assigned:
Status: Open Package: Apache2 related
PHP Version: 5.4.36 OS:
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please — but make sure to vote on the bug!
Your email address:
MUST BE VALID
Solve the problem:
35 + 32 = ?
Subscribe to this entry?

 
 [2015-01-18 12:26 UTC] phillip dot berndt at googlemail dot com
Description:
------------
If a request contains multiple Authorization headers, PHP and Apache parse them differently. If Apache does the authorization, this leads to PHP having a wrong password in $_SERVER[PHP_AUTH_PW]:

Apache uses the first header for authentication.

PHP concatenates both headers somehow, I suppose with the usual ", " in between, and then messes up base64 decoding (the source code seems to actually call uudecode()?!).

This alone does not have any security implications I can think of, therefore I'll categorize this as a simple bug. I haven't looked into the cause of the bug in the base64 decoder though.

Test script:
---------------
htaccess file:

AuthType Basic
AuthUserFile /path/to/htpasswd
AuthName "test"
Require valid-user

php script:

<?php
	header("Content-type: text/plain");
	echo urlencode($_SERVER["PHP_AUTH_PW"]);


htpasswd allows login for user:user

Expected result:
----------------
$ curl "http://path/to/page" -H "Authorization: Basic dXNlcjp1c2Vy" -H "Authorization: Basic YWRtaW46YWRtaW4=" 
user

Actual result:
--------------
$ curl "http://path/to/page" -H "Authorization: Basic dXNlcjp1c2Vy" -H "Authorization: Basic YWRtaW46YWRtaW4=" 
user%05%AB%22q%85%91%B5%A5%B8%E9%85%91%B5%A5%B8%

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-01-18 13:08 UTC] requinix@php.net
Multiple Authorization headers aren't actually permitted by the RFCs. What you're sending is equivalent to
  Authorization: Basic dXNlcjp1c2Vy, Basic YWRtaW46YWRtaW4
Base-64 ignores whitespace and other invalid characters so that gets treated like
  Authorization: Basic dXNlcjp1c2VyBasicYWRtaW46YWRtaW4
which decodes to ("user:" followed by) the crazy stuff you're seeing.
  echo urlencode(base64_decode('dXNlcjp1c2VyBasicYWRtaW46YWRtaW4'));
  // user%3Auser%05%AB%22q%85%91%B5%A5%B8%E9%85%91%B5%A5%B8

IMO since this situation is prohibited, PHP's behavior is acceptable. Apache ignoring the additional headers is reasonable because multiple Authorizations don't make sense, and PHP combining them is not unreasonable because multiple headers means necessarily that they are equivalent to one which has the values combined and comma-separated.
 [2015-01-18 16:05 UTC] phillip dot berndt at googlemail dot com
My point was not to blame anyone that the RFC is not fulfiled, but to mention that different behaviour between httpd and php isn't what any user would expect, and therefore can potentially lead to security issues if an attacker deliberately sends two authentication headers. If authentication is handled externally through Apache and PHP_AUTH_PW is populated, I'd always expect that variable to hold exactly the password that Apache recognized & accepted. In the worst case, this could mean that I use the value as if it didn't need the special care user input usually requires and then

$ curl "http://path/to/page" -H "Authorization: Basic dXNlcjp1c2Vy" -H "Authorization: Basic GEuLi4gJzsgRFJPUCBUQUJMRSB1c2VyczsgLS0A="
user..pa... '; DROP TABLE users; --

would do lot's of damage.

If you want to do anything about this, a potential solution would be to check if Authorization headers exactly match "Basic <valid base64 without any other characters in between>" and only populate the PHP_AUTH_* variables if so.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 25 12:01:31 2024 UTC