Sfoglia il codice sorgente

[HttpFoundation] HTTP Basic authentication is broken with PHP as cgi/fastCGI under Apache

Bug fix: yes
Feature addition: no
Backwards compatibility break: no
Symfony2 tests pass: yes
Fixes the following tickets: #1813
Todo: -

In order to work, add this to the .htaccess:

RewriteEngine on
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ app.php [QSA,L]
kepten 13 anni fa
parent
commit
a450d002f2

+ 35 - 3
src/Symfony/Component/HttpFoundation/ServerBag.php

@@ -16,6 +16,7 @@ namespace Symfony\Component\HttpFoundation;
  *
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ * @author Robert Kiss <kepten@gmail.com>
  */
 class ServerBag extends ParameterBag
 {
@@ -32,10 +33,41 @@ class ServerBag extends ParameterBag
             }
         }
 
-        // PHP_AUTH_USER/PHP_AUTH_PW
         if (isset($this->parameters['PHP_AUTH_USER'])) {
-            $pass = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : '';
-            $headers['AUTHORIZATION'] = 'Basic '.base64_encode($this->parameters['PHP_AUTH_USER'].':'.$pass);
+            $headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER'];
+            $headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : '';
+        } else {
+            /*
+             * php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default
+             * For this workaround to work, add this line to your .htaccess file:
+             * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+             *
+             * A sample .htaccess file:
+             * RewriteEngine On
+             * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+             * RewriteCond %{REQUEST_FILENAME} !-f
+             * RewriteRule ^(.*)$ app.php [QSA,L]
+             */
+
+            $authorizationHeader = null;
+            if (isset($this->parameters['HTTP_AUTHORIZATION'])) {
+                $authorizationHeader = $this->parameters['HTTP_AUTHORIZATION'];
+            } elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) {
+                $authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION'];
+            }
+
+            // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW
+            if (null !== $authorizationHeader) {
+                $exploded = explode(':', base64_decode(substr($authorizationHeader, 6)));
+                if (count($exploded) == 2) {
+                    list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded;
+                }
+            }
+        }
+
+        // PHP_AUTH_USER/PHP_AUTH_PW
+        if (isset($headers['PHP_AUTH_USER'])) {
+            $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']);
         }
 
         return $headers;

+ 2 - 2
src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php

@@ -56,7 +56,7 @@ class BasicAuthenticationListener implements ListenerInterface
     {
         $request = $event->getRequest();
 
-        if (false === $username = $request->server->get('PHP_AUTH_USER', false)) {
+        if (false === $username = $request->headers->get('PHP_AUTH_USER', false)) {
             return;
         }
 
@@ -71,7 +71,7 @@ class BasicAuthenticationListener implements ListenerInterface
         }
 
         try {
-            $token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->server->get('PHP_AUTH_PW'), $this->providerKey));
+            $token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
             $this->securityContext->setToken($token);
         } catch (AuthenticationException $failed) {
             $this->securityContext->setToken(null);

+ 40 - 1
tests/Symfony/Tests/Component/HttpFoundation/ServerBagTest.php

@@ -40,6 +40,8 @@ class ServerBagTest extends \PHPUnit_Framework_TestCase
             'CONTENT_LENGTH' => '0',
             'ETAG' => 'asdf',
             'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => 'bar',
         ), $bag->getHeaders());
     }
 
@@ -47,6 +49,43 @@ class ServerBagTest extends \PHPUnit_Framework_TestCase
     {
         $bag = new ServerBag(array('PHP_AUTH_USER' => 'foo'));
 
-        $this->assertEquals(array('AUTHORIZATION' => 'Basic '.base64_encode('foo:')), $bag->getHeaders());
+        $this->assertEquals(array(
+            'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => ''
+        ), $bag->getHeaders());
+    }
+
+    public function testHttpBasicAuthWithPhpCgi()
+    {
+        $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar')));
+
+        $this->assertEquals(array(
+            'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => 'bar'
+        ), $bag->getHeaders());
+    }
+
+    public function testHttpBasicAuthWithPhpCgiRedirect()
+    {
+        $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar')));
+
+        $this->assertEquals(array(
+            'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => 'bar'
+        ), $bag->getHeaders());
+    }
+
+    public function testHttpBasicAuthWithPhpCgiEmptyPassword()
+    {
+        $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:')));
+
+        $this->assertEquals(array(
+            'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
+            'PHP_AUTH_USER' => 'foo',
+            'PHP_AUTH_PW' => ''
+        ), $bag->getHeaders());
     }
 }