Autenticazione HTTP usando PHP

È possibile usare la funzione header() per inviare un messaggio di "Authentication Required" al browser dell'utente, provocando quindi l'apertura di una finestra contenente una richiesta di Nome utente/Password. Una volta che l'utente ha compilato i campi nome utente e password, l'URL contenente lo script PHP verrà richiamato nuovamente usando le variabili predefinite, PHP_AUTH_USER, PHP_AUTH_PW e AUTH_TYPE impostate con nome, password e tipo di autenticazione. Queste variabili predefinite possono essere trovate negli array $_SERVER e $HTTP_SERVER_VARS. Saranno supportati entrambi i metodi di autenticazione, "Basic" e "Digest" (a partire dal PHP 5.1.0). Fare riferimento alla funzione header() per ulteriori informazioni.

Nota: Nota sulla versione di PHP

Le variabili autoglobali, come $_SERVER, esistono a partire da PHP » 4.1.0.

Un frammento di script di esempio che richiede l'autenticazione da parte del browser su una pagina, potrebbe essere il seguente:

Example #1 Esempio di Autenticazione HTTP "Basic"

<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
    
header('WWW-Authenticate: Basic realm="Il mio realm"');
    
header('HTTP/1.0 401 Unauthorized');
    echo 
'Messaggio da inviare se si preme il tasto Cancel';
    exit;
} else {
    echo 
"<p>Ciao {$_SERVER['PHP_AUTH_USER']}.</p>";
    echo 
"<p>Hai inserito {$_SERVER['PHP_AUTH_PW']} come tua password.</p>";
}
?>

Example #2 Esempio di autenticazione HTTP "Digest"

Questo esempio illustra come implementare una autenticazione Digest. Per maggiori dettagli vedere » RFC 2617.

<?php
$realm 
'Restricted area';

//user => password
$users = array('admin' => 'mypass''guest' => 'guest');


if (empty(
$_SERVER['PHP_AUTH_DIGEST'])) {
    
header('HTTP/1.1 401 Unauthorized');
    
header('WWW-Authenticate: Digest realm="'.$realm.
           
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');

    die(
'Text to send if user hits Cancel button');
}


// analisi della variabile PHP_AUTH_DIGEST
if (!($data http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
    !isset(
$users[$data['username']]))
    die(
'Wrong Credentials!');


// generazione di una risposta valida
$A1 md5($data['username'] . ':' $realm ':' $users[$data['username']]);
$A2 md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);

if (
$data['response'] != $valid_response)
    die(
'Wrong Credentials!');

// Ok, utente/password validi
echo 'Your are logged in as: ' $digest['username'];


// funzione che analizza l'header http auth
function http_digest_parse($txt)
{
    
// protezione contro i dati mancanti
    
$needed_parts = array('nonce'=>1'nc'=>1'cnonce'=>1'qop'=>1'username'=>1'uri'=>1'response'=>1);
    
$data = array();
    
$keys implode('|'array_keys($needed_parts));

    
preg_match_all('@(' $keys ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@'$txt$matchesPREG_SET_ORDER);

    foreach (
$matches as $m) {
        
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
        unset(
$needed_parts[$m[1]]);
    }

    return 
$needed_parts false $data;
}
?>

Nota: Note sulla compatibilitΓ 

Fare molta attenzione quando si scrive codice per le intestazioni HTTP. Per ottenere la massima compatibilitΓ  con tutti i client, la parola-chiave "Basic" deve essere scritta con una "B" maiuscola, la stringa realm deve essere racchiusa in virgolette doppie (non singole), ed esattamente uno spazio deve precedere il codice 401 nella linea di intestazione HTTP/1.0 401. I parametri di autenticazione devono essere separati da virgole come si vede nell'esempio qui sopra.

Invece di stampare semplicemente PHP_AUTH_USER e PHP_AUTH_PW, probabilmente si vorrΓ  controllare la validitΓ  dello username e della password. Probabilmente inviando una chiamata al database, o cercando un utente in un file dbm.

Si faccia attenzione ad alcune versioni di Internet Explorer. Sembra che siano molto schizzinosi riguardo all'ordine delle intestazioni. Inviare l'intestazione WWW-Authenticate prima dell'intestazione HTTP/1.0 401 sembra sistemare le cose per il momento.

A partire da PHP 4.3.0, al fine di prevenire che qualcuno scriva uno script che rivela la password di una pagina che era stata autenticata tramite un tradizionale meccanismo esterno, le variabili PHP_AUTH non verranno impostate se Γ¨ abilitata l'autenticazione esterna per quella determinata pagina e modalitΓ  sicura Γ¨ abilitato. CiΓ² nonostante, la variabile REMOTE_USER puΓ² essere usata per identificare un utente autenticato esternamente. Quindi, si puΓ² usare $_SERVER['REMOTE_USER'].

Nota: Nota sulla Configurazione

PHP usa la presenza di una direttiva AuthType per determinare se viene utilizzata l'autenticazione esterna.

Si fa notare, perΓ², che quanto scritto sopra non impedisce a qualcuno che controlla un URL non autenticato di sottrarre password da URL autenticati presenti sullo stesso server.

Sia Netscape Navigator che Internet Explorer cancellano la cache locale della finestra del browser, per quanto riguarda il realm, al ricevimento di una risposta 401 da parte del server. Questo Γ¨ effettivamente un meccanismo di "log out" per l'utente, che lo forza a reinserire lo username e la password. Alcune persone usano questo per fare il "time out" dei login, o per rendere disponibili bottoni di "log-out".

Example #3 Esempio di Autenticazione HTTP che impone l'inserimento di nuovo username/password

<?php
function authenticate() {
    
header('WWW-Authenticate: Basic realm="Prova del Sistema di Autenticazione"');
    
header('HTTP/1.0 401 Unauthorized');
    echo 
"Per poter accedere a questa risorsa occorre inserire una coppia login e password valide\n";
    exit;
}
 
if (!isset(
$_SERVER['PHP_AUTH_USER']) ||
    (
$_POST['SeenBefore'] == && $_POST['OldAuth'] == $_SERVER['PHP_AUTH_USER'])) {
    
authenticate();
} else {
    echo 
"<p>Benvenuto: " htmlspecialchars($_SERVER['PHP_AUTH_USER']) . "<br />";
    echo 
"Vecchio: " htmlspecialchars($_REQUEST['OldAuth']);
    echo 
"<form action='' method='post'>\n";
    echo 
"<input type='hidden' name='SeenBefore' value='1' />\n";
    echo 
"<input type='hidden' name='OldAuth' value=\"" htmlspecialchars($_SERVER['PHP_AUTH_USER']) . "\" />\n";
    echo 
"<input type='submit' value='Ri autentifica' />\n";
    echo 
"</form></p>\n";
}
?>

Questo comportamento non Γ¨ richiesto dallo standard di autenticazione HTTP Basic, quindi non si dovrebbe mai fare affidamento su di esso. Test effettuati con Lynx mostrano che Lynx non cancella le credenziali di autenticazione al ricevimento del codice di risposta 401 da parte del server, quindi, premendo indietro e avanti nuovamente, darΓ  nuovamente accesso alla risorsa, ammesso che le rispettive richieste di credenziali non siano cambiate. L'utente puΓ² perΓ² premere il tasto '_' al fine di cancellare le sue informazioni di autenticazione.

Si noti anche che, prima di PHP 4.3.3, l'Autenticazione HTTP non funzionava usando il server IIS di Microsoft e con la versione CGI di PHP a causa di una limitazione di IIS. Al fine di farla funzionare in PHP 4.3.3+, si deve modificare la configurazione di IIS "Directory Security". Cliccare su "Edit" e selezionare solo "Anonymous Access", tutti gli altri campi devono essere lasciati deselezionati.

Un'altra limitazione Γ¨ che se si usa il modulo IIS (ISAPI) e PHP 4, non si possono usare le variabili PHP_AUTH_* ma, al contrario, la variabile HTTP_AUTHORIZATION Γ¨ disponibile. Per esempio, si consideri il seguente codice: list($user, $pw) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));

Nota: Nota su IIS:
Al fine di fare funzionare la Autenticazione HTTP con IIS, la direttiva PHP cgi.rfc2616_headers deve essere impostata al valore 0 (il valore predefinito).

Nota:

Se Γ¨ abilitato safe mode viene aggiunto lo uid dello script al realm dell'header WWW-Authenticate.