Viewed   102 times

I'm trying to use Basic HTTP Authentication and followed the example on the PHP manual page. But it doesn't work for me. The variable $_SERVER['PHP_AUTH_USER'] doesn't seem to be set. When a user try to log in, the user is prompted whith a new login-dialog. The server is running PHP 5.3.3 and I have tried with Google Chrome and Internet Explorer.

Here is the simple example that I used:

<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm="Jonas Realm"');
    header('HTTP/1.0 401 Unauthorized');
    echo 'User pressed Cancel';
    exit;
} else {
    echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
    echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as you password.</p>";
}
?>

What is wrong? How can I use Basic HTTP Authentication in PHP?

 Answers

4

How is PHP being run? If it's through Apache mod_cgi, I'm afraid you cannot get hold of the authentication information at all. Apache won't pass it to CGI apps unless you compile it with the SECURITY_HOLE_PASS_AUTHORIZATION flag. (Whether it's actually a security hole or not depends on whether other users on your server have a lower or greater privilege than your website users.)

If it's IIS CGI, you have to ensure that only ‘Anonymous’ directory access is configured, or IIS will try to step in and authenticate credentials itself.

echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";

HTML-injection security hole (well, if it worked). Use htmlspecialchars(). Man, is that PHP doc page full of highly questionable advice and code.

You can try to look at $_SERVER['HTTP_AUTHORIZATION'] as well, though I suspect it's the mod_cgi issue in which case neither variable will be present and you will have to resort to cookie-based login instead. Or change to a host with a more modern approach to PHP hosting, such as FastCGI or mod_php.

Saturday, November 19, 2022
 
kipodi
 
1

Yes, that looks like a valid workflow for the situation you described, and those Authenticate headers seem to be in the correct format.

It's interesting to note that it's possible, albeit unlikely, for a given connection to involve multiple proxies that are chained together, and each one can itself require authentication. In this case, the client side of each intermediate proxy would itself get back a 407 Proxy Authentication Required message and itself repeat the request with the Proxy-Authorization header; the Proxy-Authenticate and Proxy-Authorization headers are single-hop headers that do not get passed from one server to the next, but WWW-Authenticate and Authorization are end-to-end headers that are considered to be from the client to the final server, passed through verbatim by the intermediaries.

Since the Basic scheme sends the password in the clear (base64 is a reversible encoding) it is most commonly used over SSL. This scenario is implemented in a different fashion, because it is desirable to prevent the proxy from seeing the password sent to the final server:

  • the client opens an SSL channel to the proxy to initiate the request, but instead of submitting a regular HTTP request it would submit a special CONNECT request (still with a Proxy-Authorization header) to open a TCP tunnel to the remote server.
  • The client then proceeds to create another SSL channel nested inside the first, over which it transfers the final HTTP message including the Authorization header.

In this scenario the proxy only knows the host and port the client connected to, not what was transmitted or received over the inner SSL channel. Further, the use of nested channels allows the client to "see" the SSL certificates of both the proxy and the server, allowing the identity of both to be authenticated.

Thursday, December 1, 2022
 
2

I found a solution here: http://wiki.metawerx.net/wiki/SecuringYourSiteWithContainerManagedSecurity

The page describes how to define your own META-INF/context.xml pointing to your own WEB-INF/users.xml. Unfortunately, the link to the users.xml file has to be absolute, and I do not want to make any assumptions on the OS/filesystem paths in my config files.

Here is my current WEB-INF/web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd"
    version="2.5">

    <display-name>SuperCoolTool</display-name>
    <description>What an awesome app!</description>

    <security-role>
        <role-name>manager</role-name>
    </security-role>
    <security-role>
        <role-name>keyuser</role-name>
    </security-role>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>
                Entire Application
            </web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>keyuser</role-name>
            <role-name>manager</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>Evaluation Area</realm-name>
    </login-config>

</web-app> 

An matching META-INF/context.xml would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <Realm className="org.apache.catalina.realm.MemoryRealm"
           pathname="[PATH-TO-YOUR-WEBAPP]/WEB-INF/users.xml"/>
</Context>
Monday, September 12, 2022
4

Try implementing your own RequestFactory in order to achieve preemptive authentication.

public class PreEmptiveAuthHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {

public PreEmptiveAuthHttpRequestFactory(DefaultHttpClient client) {
    super(client);
}

@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
    AuthCache authCache = new BasicAuthCache();
    BasicScheme basicAuth = new BasicScheme();
    HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort());
    authCache.put(targetHost, basicAuth);
    BasicHttpContext localcontext = new BasicHttpContext();
    localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);

    return localcontext;
}
}

An then just use it:

HttpComponentsClientHttpRequestFactory requestFactory = new PreEmptiveAuthHttpRequestFactory( newHttpClient );

Hope it helps


how to set the username and password (Copied from @bifur's comment)

You can use UserNamePasswordCredentials

UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(getUsername(),getPassword()); 
client.getCredentialsProvider().setCredentials(new AuthScope(getHost(), getPort(), AuthScope.ANY_REALM), credentials); 

And just use the client in the previous factory

HttpComponentsClientHttpRequestFactory requestFactory = new PreEmptiveAuthHttpRequestFactory(client);
Sunday, September 11, 2022
1

Since your needs sound outwith AuthComponent's originally intended design you have two options.

Firstly, if it really doesn't fit your needs, you could create and maintain your very own AuthComponent. Do this by copying /cake/libs/controller/components/auth.php to /app/controller/components/auth.php.

This would allow you to rewrite the component completely, but the downside is you will no longer receive updates to AuthComponent when you upgrade cake.

Secondly, you can extend just about anything in CakePHP using the following pattern:

// save as: /app/controllers/components/app_auth.php
App::import('Component', 'Auth');
class AppAuthComponent extends AuthComponent {
    function identify($user = null, $conditions = null) {
        // do stuff
        return parent::indentify($user, $conditions);
    }
}

.. and replace all instances of AuthComponent in your controllers with your AppAuthComponent.

  • You only need to define the methods you wish to replace.
  • You can run methods from the original AuthComponent (even ones you have redefined) at any point during your methods using parent::...
  • The method arguments should remain in the same order as the original API for consistency.
  • If you wish to add more method arguments, put them after the API ones, eg:

    function identify($user = null, $conditions = null, $custom = array()) { ... }

This approach allows you to make application-specific customisation while still using the latest methods defined in the core where necessary.

Monday, October 17, 2022
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :