Authentication over HTTP

HTTP authentication traditionally takes the form of .htaccess files scattered around various directories webmasters want to keep private. A typical .htaccess file, combined with a .htpasswd file, contains information about users that are allowed access to a directory and their password.

Even though Apache allows you to customise these permissions, the system is far from flexible - hand-editing files each time you add users, or having to group authorised users together by password is a little behind the times.

HTTP authentication is mostly just a matter of sending special HTTP headers to your client asking them to provide access codes, and it is straightforward to implement in PHP as long as you have configured PHP to run as an Apache module (see previous issue for our installation guide). Let's look at basic authentication by creating the file auth.php, which should look like this:

<?php
    if (!isset($_SERVER['PHP_AUTH_USER'])) {
        header("WWW-Authenticate: Basic realm=\"Private Area\"");
        header("HTTP/1.0 401 Unauthorized");
        // only reached if authentication fails
        print "Sorry - you need valid credentials granted access to the private area!\n";
        exit;
    } else {
        // only reached if authentication succeeds
        print "Welcome to the private area, {$_SERVER['PHP_AUTH_USER']} - you used {$_SERVER['PHP_AUTH_PW']} as your password.";
    }
?>

Braces are used inside our final print statement because we are printing out a value from inside an array, and the braces tell PHP to treat $_SERVER['PHP_AUTH_USER'] as an array variable that needs replacing with its value.

To start the authentication process, we send two HTTP headers using PHP's header() function. With header(), you can send any HTTP header you want, so long as you send them all before you send any HTML. I will be mentioning header() in several subsequent articles, but right now we are just interested in the WWW-Authenticate header and HTTP status codes.

WWW-Authenticate allows us to define the area, or _realm_, to which we are limiting access. It might be "Internet Mail Gateway", "Members Area", or, in our example, "Private Area". This realm name is usually shown to users when they are prompted for their username and password.

The second header() function sends the HTTP status "401", which means "no access". This most often means no username and password have been entered, but it may also mean the details entered were incorrect. Therefore, while WWW-Authenticate tells the browser what response is required to authenticate, the 401 header says "no entry" - you need both to perform authentication.

If your user clicks "Cancel" they should be presented with something other than a blank page. In our example above, we have the print line beginning "Sorry - you need valid" ready for this eventuality.

The last print statement, "Welcome to the private area" is for people who have authenticated successfully. All it takes to authenticate currently is a username and password - we don't check the values of the data, we just accept whatever they give us.

if (!isset($_SERVER['PHP_AUTH_USER'])) {

That line forms the crux of authentication with PHP. When users submit authentication, PHP receives the username and password as $_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'] respectively. By checking whether $_SERVER['PHP_AUTH_USER'] is set, we are saying, "Have we received an authentication username from the client?" If we have not, we send a request for authentication using WWW-Authenticate and exit the script.

When our visitors provide a username and password, the script is called again. This time the 'if' statement evaluates to true and we print out our welcome message. Most sites would want to perform some sort of username and password checking in order to make authentication worthwhile, so let us change the script to include simple credentials checking.

<?php
    if (!isset($_SERVER['PHP_AUTH_USER'])) {
        header("WWW-Authenticate: Basic realm=\"Private Area\"");
        header("HTTP/1.0 401 Unauthorized");
        print "Sorry - you need valid credentials to be granted access!\n";
        exit;
    } else {
        if (($_SERVER['PHP_AUTH_USER'] == 'paul') && ($_SERVER['PHP_AUTH_PW'] == 'hudson')) {
            print "Welcome to the private area!";
        } else {
            header("WWW-Authenticate: Basic realm=\"Private Area\"");
            header("HTTP/1.0 401 Unauthorized");
            print "Sorry - you need valid credentials to be granted access!\n";
            exit;
        }
    }
?>

The modified script above now only allows users that provide the username 'paul' and the password 'hudson'. We have two conditions combined using AND (&&), which means that the 'if' statement only evaluates to true if the username is 'paul' and the password is 'hudson'.

Our system is now more powerful, but we still need to hard code usernames and passwords for everyone we want to have access to our realm.

 

Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: Dynamic authentication >>

Previous chapter: Reading queued headers

Jump to:

 

Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.