PHP  
downloads | documentation | faq | getting help | mailing lists | | php.net sites | links | my php.net 
search for in the  
<FunctionaliteitCookies>
view the version of this page
Last updated: Tue, 22 Apr 2003

Hoofdstuk 16. HTTP authenticatie met PHP

De HTTP Authenticatie functies in PHP zijn alleen te gebruiken als PHP gebruikt wordt als Apache module en is zijn NIET te gebruiken in de CGI versie. In een PHP script is het dan mogelijk, met de header() functie, een "Authentication Required" bericht naar de browser te sturen. Dit veroorzaakt dat de browser een "Login/Password" invul window aan de gebruiker laat zien. Zo gauw de gebruiker een login naam en een password heeft ingevuld wordt de URL met het PHP script dat de header stuurde opnieuw aangeroepen. Nu zijn de variablen $PHP_AUTH_USER, $PHP_AUTH_PW en $PHP_AUTH_TYPE gevuld met de waarden die de gebruiker heeft ingevuld. Op dit moment word alleen de "Basic" authenticatie ondersteund. Zie de header() functie voor meer informatie.

Dit voorbeeld script dwingt gebruikers authenticatie af:

Voorbeeld 16-1. HTTP Authentication voorbeeld

<?php
  if(!isset($PHP_AUTH_USER)) {
    Header("WWW-Authenticate: Basic realm=\"Mijn Realm\"");
    Header("HTTP/1.0 401 Unauthorized");
    echo "Tekst om te sturen als de gebruiker op Annuleren klikt.\n";
    exit;
  } else {
    echo "Hallo $PHP_AUTH_USER.<P>";
    echo "Je gebruikte $PHP_AUTH_PW als je password.<P>";
  }
?>

In plaats van slechts de ingevoerde waarden aan de gebruiker te laten zien, wil je waarschijnlijk $PHP_AUTH_USER en $PHP_AUTH_PW gebruiken om de gebruiker te valideren tegen bijvoorbeeld een database.

Kijk uit voor buggy Internet Explorer browsers. Sommige zijn erg nauwkeurig over de volgorde dat de headers gestuurd worden. Door eerst de WWW-Authenticate header te sturen en dan de HTTP/1.0 401 header lijkt het probleem opgelost te zijn.

Om te voorkomen dat iemand een script schrijft die de wachtwoorden achterhaald van iemand die al eerder geauthenticeerd was door middel van het externe authenticatie systeem worden de PHP_AUTH_* variabelen niet gevuld als er gebruik word gemaakt van het externe authenticatie systeem voor die bepaalde pagina. In dit geval kan de variabele $REMOTE_USER gebruikt worden om de extern geauthenticeerde gebruiker te identificeren.

Dit weerhoudt iemand er echter niet van om wachtwoorden te stelen via een niet-geauthenticeerde URL op dezelfde server.

Zowel Netscape als Internet Explorer zullen hun huidige cache in het huidige Window legen wanneer ze een 401 header van de server krijgen. Op deze manier kan je op een efficiente manier een gebruiker "uit loggen". Sommige mensen gebruiken dit om een login te laten verlopen of gebruiken dit met een "log-out" knop.

Voorbeeld 16-2. HTTP Authentication voorbeeld welke een nieuwe loginnaam en wachtwoord vereist.

<?php
  function authenticate() {
    Header( "WWW-Authenticate: Basic realm=\"Test Authentication Systeem\"");
    Header( "HTTP/1.0 401 Unauthorized");
    echo "Je moet een geldige login en wachtwoord opgeven om bij ".
    "deze pagina te komen\n";
    exit;
  }
 
  if(!isset($PHP_AUTH_USER) || ($EerderGezien == 1 && !strcmp($VorigeNaam, $PHP_AUTH_USER)) ) {
   authenticate();
  } 
  else {
   echo "Welcome: $PHP_AUTH_USER<BR>";
   echo "Old: $VorigeNaam";
   echo "<FORM ACTION=\"$PHP_SELF\" METHOD=POST>\n";
   echo "<INPUT TYPE=HIDDEN NAME=\"EerderGezien\" VALUE=\"1\">\n";
   echo "<INPUT TYPE=HIDDEN NAME=\"VorigeNaam\" VALUE=\"$PHP_AUTH_USER\">\n";
   echo "<INPUT TYPE=Submit VALUE=\"Opnieuw inloggen\">\n";
   echo "</FORM>\n";
  }
?>

Dit gedrag is niet vereist voor de HTTP Basic authenticatie standaard, dus je moet hier nooit op vertrouwen. Tests met de Lynx browser wijzen uit dat Lynx zijn cache NIET leegt als hij een 401 header van de server krijgt. Dus als je 'back' en 'forward' gaat krijg je gewoon de pagina te zien met de eerder ingevoerde waarden (Tenzij de login voorwaarden zijn gewijzigd).

Let op: Dit werkt niet met Microsoft's IIS server en de CGI versie van PHP vanwege een gebrek in IIS.



User Contributed Notes
HTTP authenticatie met PHP
add a note add a note
fredrick-realm at home dot com
21-Jul-1999 10:02

A few notes on authentication in which it's possible I overlooked some things.  Considering a prior post about using the same salt for all users so you can match passwords; I think it would be better to not do so, as you can figure out the salt from the password and match.  (Example, salt in DES if I'm not mistaken is the first 2 characters)
 Something I've been trying to figure out is secure apache module PHP on a multi-user server.  
 Delima (with postgres)- any user can write a PHP page to read another users databases.  Set your database to connect using username and password, and any user can read your username and password from wherever you place them.  (use PHP function to read it and as it has to be readable by your web process for you to read it, they can)  
 The closest I've come to a solution for this is to run php as a CGI module with suexec or cgiwrap.  
 Hopefuly someone else has a better solution; otherwise, something to think about before you think of your databases as being secure with php interfacing to them.

auke at muze dot nl
18-Dec-1999 01:42

Someone gave me a simple solution to the 'logout' problem: add some sort of timestamp to the basic realm you send in the WWW_Authenticate header. Mine now is: $realm="RealmName ( ".strftime("%c",time())." )";. (btw: the problem was: 1) IE4 asks for the page one more time after a 401, defeating sending a 401 once to force a user to log on again. and 2) IE4 remembers the password, and puts it default in the logon window. Changing the realm solves these problems, not the 'logon failed' message of NS though).
rratboy at pobox dot com
09-Feb-2000 06:59

I had the same problem as above (that is, with apache I can't get the auth info). The solution I found goes like this:

$headers = getallheaders();
$auth=$headers[authorization];
if ($auth=='') { $auth=$headers[Authorization]; }

if($auth=='')
{
Header("WWW-Authenticate: Basic realm=\"$PROG_NAME\"");
Header("HTTP/1.0 401 Unauthorized");
}

list($user, $pass) = explode(":", base64_decode(substr($auth, 6)));

tigran at freenet dot am
19-May-2000 09:31

Here is a code for the public sites enabling both logout bottom and timeout using php+mysql. Working for both browsers.
The part "required" for each user protected page:

<?
function auth () {
      Header("WWW-Authenticate: Basic realm=\"ArmFN public site\"");
       Header("HTTP/1.0 401 Unauthorized");
       echo "You have to authentificate yourself first \n";
      exit;
}

mysql_connect("localhost","train","") or die("Unable to connect to SQL server");
mysql_select_db( "train") or die( "Unable to select database");

if(!isset($PHP_AUTH_USER)) {

$timeout = mktime(date(G),date(i)+10,0,date("m"),date("d"),date("Y"));
mysql_query("update users set login='$timeout' where id='$user' and pasw='$pass'") or die("k");

auth();

} else {

  $pass = $PHP_AUTH_PW;
   $user = $PHP_AUTH_USER;

$nowtime = mktime(date(G),date(i),0,date("m"),date("d"),date("Y"));
$quer2 = mysql_query("select * from users where id='$user' and pasw='$pass' and login > '$nowtime'") or die("kuk2");

   if (mysql_num_rows($quer2) == "0") {
$timeout = mktime(date(G),date(i)+10,0,date("m"),date("d"),date("Y"));
mysql_query("update users set login='$timeout' where id='$user' and pasw='$pass'") or die("k");

auth();
}
}
?>

You can have a "logout" bottom with hidden $go="logout" form element and then have somewhere this part:

if ($do == "logout") {
mysql_connect("localhost","train","") or die("Unable to connect to SQL server");
mysql_select_db( "train") or die( "Unable to select database");
mysql_query("update users set login=0 where id='$PHP_AUTH_USER' and pasw='$PHP_AUTH_PW'") or die("k");
}

owld at mail dot ru
30-Aug-2000 10:04

Good day.I've solved a problem where IE4 asks for the age one more time after a 401, defeating sending a 401 once to force a user to log on again.

 function  authenticate()  {
  setcookie("noauth","");
   Header( "WWW-authenticate:  Basic realm=\"test\"");
  Header( "HTTP/1.0  401  Unauthorized");
echo "You must enter user name";
  exit ;
 }
 if  (   !isset($PHP_AUTH_USER) ||  ($logoff==1) && $noauth=="yes"  )   {
authenticate();
 }

And logoff link -

<a href="samehtml.phtml?logoff=1">Logoff</a></td>

Dmitry Alyekhin

marcel at humanique dot com
16-Oct-2000 10:01

The new Mozilla browser doesn't seem to like the switched authentication lines.

This doesn't work (I have build 2000101308):

Header( "WWW-authenticate: Basic realm=\"test\"");
Header( "HTTP/1.0 401 Unauthorized");

The first time you authenticate all seems ok, but the second time it always returns unauthorized.

This works as it should:

Header( "HTTP/1.0 401 Unauthorized");
Header( "WWW-authenticate: Basic realm=\"test\"");

yasuo_ohgaki at hotmail dot com
10-Mar-2001 09:19

I suggest to read RFC2617 (HTTP Authentication: Basic and Digest Access Authentication) and related RFCs.
k u d o s at t e l u s p l a n e t dot n e t
05-Apr-2001 06:19

Thanks to [email protected] for the rfc note needed to solve this one. This looks like it flushed out the authentication headers on both Netscape and IE:
Header("WWW-Authenticate: Basic realm=\"Whatever Realm\", stale=FALSE");

philip at cornado dot com
17-May-2001 10:55

You may enjoy this tutorial :

eezyeee at yahoo dot com
12-Jun-2001 10:53

This is a good resource for setting up htaccess schemes:



The windows version of apache comes with htpasswd.exe in the apache\bin directory.

The only thing that present problems is you have to change your .htaccess file to point to the created password file (ie C:\directory\passwords.file)....so if you transfer the file back to a *nix server it wont find your file.

One (temporary) workaround is changing your local httpd.conf file to point to a different access file:

AccessFileName htaccess.

You just have to make sure to syncronize your access files.

Im not sure if you can point your htaccess to two password files??
AuthName "restricted stuff"
AuthType Basic
AuthUserFile /usr/local/etc/httpd/users
AuthUserFile C:\directory\password.file
require valid-user

jonhaynes at bigfoot dot com
28-Jan-2002 06:25

Restrict access by username, password AND ip address:

<?
function authenticate() {
header("WWW-Authenticate: Basic realm=\":-!\"");
header("HTTP/1.0 401 Unauthorized");
print("You must enter a valid login username and password
to access this resource.\n");
exit;
}
if(!isset($PHP_AUTH_USER)){ authenticate(); }
else {
$c=mysql_pconnect("server.name","user","password");
mysql_select_db("dbname",$c);
$q=sprintf("SELECT username,password FROM authenticateTable
WHERE username='%s' AND password=PASSWORD('%s')
AND ipaddress='%s'",
$PHP_AUTH_USER,$PHP_AUTH_PW,$REMOTE_ADDR);
$q=mysql_query($q);
if(mysql_num_rows($q)==0){ authenticate(); }
}
?>

sjeffrey at inquesis dot com
29-Jan-2002 10:00

To get it to work with IIS try using this code before setting your "$auth = 0" and the "if (isset($PHP_AUTH_USER) && isset($PHP_AUTH_PW))"

//////////////////////////////////////////

if ($PHP_AUTH_USER == "" && PHP_AUTH_PW == "" && ereg("^Basic ", $HTTP_AUTHORIZATION))
{
list($PHP_AUTH_USER, $PHP_AUTH_PW) =
   explode(":", base64_decode(substr($HTTP_AUTHORIZATION, 6)));
}

//////////////////////////////////////////

It worked for me on IIS 5 and PHP 4 in ISAPI

lenny at phpkingdom dot com
22-Feb-2002 07:09

I tried the method posted by
[email protected] for a logout feature, which seems to be a problem for users of http authentication. Tigran's method is perfect, except that after you log out, you can STILL access the pages by clicking on "cancel" when prompted again by the Java window.
This will trigger the 401 error. But it will also create an entry in the history folder.

You will notice the "forward" button on your browser becomes clickable. You only have to click on the that "forward" button to be able to access the protected pages.

I have found a solution for this problem by using a little Javascript to refresh to another page.

Please go to my website for details:

louis dot carlier at ngroups dot com
24-May-2002 03:22

The definitive HTTP authorization code:

function login_error()
{
echo "error - login process failed."
}

if (!isset($PHP_AUTH_USER))
{
header("WWW-Authenticate: Basic realm=\"Mosaic Authorization process\"");
header("HTTP/1.0 401 Unauthorized");

//Result if user hits cancel button
login_error();
}
else
{

//check the login and password
if('=>test on login and password<=')
{
 //User is logged
 ...
 ...
}
else
{
 //This re-asks three times the login and password.
 header( "WWW-Authenticate: Basic realm=\"Test Authentication System\"");
header("HTTP/1.0 401 Unauthorized");

 //Result if user does not give good login and pass
 login_error();
}
}

dowlingw at bigfoot dot com
02-Jun-2002 06:29

Using the same salt for all things is a bad idea.

Use the first two letters of the username - this also makes moving to other .htaccess based systems easier :)

05-Jun-2002 09:08
A more elegant way to force a new name/password, cf. example 17-2 (if you don't mind passing the old user in the query string):

<?
if (isset($PHP_AUTH_USER))
{
if (!isset($prev_user))
{
header("Location: );
exit;
}
else
{
if ($PHP_AUTH_USER == $prev_user)
{
header('WWW-Authenticate: Basic realm="Secure"');
header('HTTP/1.0 401 Unauthorized');
exit;
}
}
}
else
{
header('WWW-Authenticate: Basic realm="Secure"');
header('HTTP/1.0 401 Unauthorized');
exit;
}
?>

The final set of headers is necessary because some browsers seem to unset $PHP_AUTH_USER when the location header is sent.

admin at creationfarm dot com
07-Aug-2002 06:24

I have written a class for HTTP Authentication in PHP. Anyone looking for a shortcut can get it from:

Feel free to make suggestions and contributions to the class. It needs some improvement.

robdemos at cistron dot nl
09-Aug-2002 04:09

Hello all,

I've just found a great article about authentification in php with and without the apache environment, it's as flexible as can be and it works really great and secure.

See for yourself at

yaroukh at email dot cz
12-Jan-2003 11:13

spamsyntax:
In Mozilla this doesn't work. Log in, then click "logout", cancel the pop-up window, go Back - you are logged in again ...
(Mozilla 1.2, Win32)

yaroukh at email dot cz
12-Jan-2003 11:42

Here is my solution - works in MSIE and Mozilla.

I use http-authentication only for the first time user accesses his
private page; after valid username and password are provided, he is
recognized using his sessionID and ip ...
the reasons are following:
1) when users changes his password it is not required instantly
  (I find this quite comfortable)
2) auto-login function works fine (unless user click logout)

And here's how i works ...

The "trick" is to pass a temporary username+password to the browser.
(I call it "temporary" because no user account matching these
parameters is neccessary.)

The most essential thing is the following link on user's private page:

===
<?  $url = ".
       $username.     // see note 1
       ":".
       Session_ID().  // see note 2
      "@localhost/".PROJECT_NAME."/logout.phtml";
?>
<a href="<?=$url?>">logout</a>
===

1) we pass the actual username because MSIE uses this username as
  a "default pre-fill" for the login-window and some hash-string
 would confuse the users.
2) the temporary password is not too important, but there are
  two things we expect from it:
  a) we need to know this string in the logout.phtml script
  b) the string definetely should not match the user's password
     (otherwise user gets logged back instantly); using current
     Session_ID() we are pretty sure this won't happen

This link causes that the temporary login-params are available in
the logout.phtml script.
Using "www-authenticate" header in the logout.phtml script we force
the browser to accept our temporary login-params. (I suppose browser
actually repeats the request and the next time it checks
the login-params sent in the URL; but this is only my guess and
it is not important.)

The logout.phtml code:
===
<?  $query = "UPDATE users SET sessionID = NULL ".
       "WHERE sessionID = '".Session_ID()."'";
    $mysql->query($query);
    // because we (me :o) use the sessionID and the ip for
    // the identification we need to clean the sessionID; (I found it
    // a little bit easier to destroy the sessionID in the db than
    // unsetting the cookie and/or destroying+restarting
    // the current session)

    if($PHP_AUTH_PW != Session_ID()) {
       // keep asking for the login-params untill PHP_AUTH_PW returned
       // by the browser matches the current Session_ID() (which means
       // that the browser accepted the temporary login-params
       // we sent to it AND FORGOT THE REAL ONES)

       Header("HTTP/1.0 401 Unauthorized");
       Header("WWW-Authenticate: Basic realm=\"".PROJECT_NAME."\"");
  }
?>
<html>
   <head>
       <meta http-equiv="author" content="yaroukh at email dot cz">
      <title><?=PROJECT_NAME?></title>
       <link rel="stylesheet" href="style.css" type="text/css">
   </head>
   <body>
       <a href=">/main.phtml">continue</a>
  </body>
</html>
===

About the "continue" link: the link is not too important, but using it
we can get rid off the temporary login-params which wouldn't look
too aesthetically in the address-bar. :o)

emmanuel dot keller at net2000 dot ch
14-Jan-2003 09:14

Some servers won't support the HTTP1.0 specification and will give an error 500 (for instance). This happened with a server where I uploaded an authentication script.

If it happens, you can try the HTTP1.1 header syntax :

header("WWW-Authenticate: Basic realm=\"My Realm\"");
header('status: 401 Unauthorized');

JKi
14-Feb-2003 10:38

To clear HTTP authentication cache in Internet Explorer (6 SP1 and later), use "ClearAuthenticationCache" command.

document.execCommand("ClearAuthenticationCache");

25-Apr-2003 12:02
@emmanuel dot keller at net2000 dot ch:
The behaviour you report bases on the fact that PHP installed as a CGI program must send CGI status messages instead of HTTP return codes. Therefore, anywhere you normally send "HTTP/1.0 xyz", you have to send "Status: xyz".

h1suzuki at hotmail dot com
11-May-2003 07:27

my solution to use SSL for password encryption, because the password is sent to web server as plain text.
insert the following code snipet into the top of secure page.

if (!isset($_SERVER['HTTPS'] || $_SERVER['HTTPS']!="on") {
   header("Location: https://$_SERVER['SERVER_NAME']".
                                  $_SERVER['REQUEST_URI']);
   exit;
}
if (!isset($_SERVER['PHP_AUTH_USER'] || authenticate()) {
  header('WWW-Authenticate: Basic realm="secure"');
  header('HTTP/1.0 401 Unauthorized');
   echo 'Authorization Required.';
   exit;
}

first, redirect to the same URI but SSL-enabled page.  then, do authentication.

add a note add a note

<FunctionaliteitCookies>
 Last updated: Tue, 22 Apr 2003
show source | credits | mirror sites 
Copyright © 2001-2003 The PHP Group
All rights reserved.
This mirror generously provided by: /
Last updated: Sat May 24 21:09:36 2003 CEST