Forum und email

Autenticação HTTP com PHP

Autenticação via HTTP no PHP só é disponível quando o mesmo for executador como módulo do Apache e, portanto, não é disponível na versão CGI. Em um script PHP no módulo Apache, é possível usar a função header() para enviar uma janela de entrada "Authentication Required" que causará que o browser cliente abra um diálogo de entrada de usuário/senha. Uma vez que o usuário preencheu seu nome de usuário e senha, a URL contendo o script PHP será chamada de novo com as variáveis pré-definidas PHP_AUTH_USER, PHP_AUTH_PW, e AUTH_TYPE contendo o nome de usuário, senha e tipo de autenticação, respectivamente. Essas variáveis pré-definidas são achadas nos arrays $_SERVER e $HTTP_SERVER_VARS. Somente autenticação "Basic" (Básica) é suportada. Veja a função header() para mais informações.

Nota: Nota de Versão do PHP Superglobais, como o $_SERVER, tornaram-se disponíveis no PHP » 4.1.0. $HTTP_SERVER_VARS está disponível desde o PHP 3.

Um script de exemplo que forçaria autenticação do cliente em uma página:

Example#1 Exemplo de Autenticação HTTP

<?php
  
if (!isset($_SERVER['PHP_AUTH_USER'])) {
    
header('WWW-Authenticate: Basic realm="My Realm"');
    
header('HTTP/1.0 401 Unauthorized');
    echo 
'Texto a ser enviado caso o usuário aperte o botão Cancelar';
    exit;
  } else {
    echo 
"<p>Olá, {$_SERVER['PHP_AUTH_USER']}.</p>";
    echo 
"<p>Você digitou {$_SERVER['PHP_AUTH_PW']} como sua senha.</p>";
  }
?>

Example#2 Exemplo de Autenticação HTTP do tipo Digest

Esse exemplo mostra como implementar um script simples Autenticação HTTP do tipo Digest. Para mais informação leia o » 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');
        }


        
// analyze the PHP_AUTH_DIGEST variable
        
if (!($data http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
            !isset(
$users[$data['username']]))
            die(
'Wrong Credentials!');


        
// generate the valid response
        
$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, valid username & password
        
echo 'Your are logged in as: ' $data['username'];


        
// function to parse the http auth header
        
function http_digest_parse($txt)
        {
            
// protect against missing data
            
$needed_parts = array('nonce'=>1'nc'=>1'cnonce'=>1'qop'=>1'username'=>1'uri'=>1'response'=>1);
            
$data = array();

            
preg_match_all('@(\w+)=(?:([\'"])([^\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: Nota de Compatibilidade Por favor, tenha cuidado quando codificar as linhas do cabeçalho do HTTP. Para garantir compatibilidade máxima com todos os clientes, a palavra chave "Basic" deve ser escrita com "B" maiúsculo, deve se usar aspas duplas (não simples) em volta da string realm, e exatamente um espaço deve preceder o código 401 na linha do cabeçalho HTTP/1.0 401. Parâmetros de autenticação devem ser separados por vírgula, como visto no exemplo acima.

Ao invés de simplesmente mostrar o valor de PHP_AUTH_USER e PHP_AUTH_PW, como foi feito no exemplo acima, você deve querer checar a validade do nome de usuário e a senha. Talvez enviando uma query para um banco de dados, ou procurando o usuário em um arquivo dbm.

Cuidado com browsers Internet Explorer bugados por aí. Eles parecem muito minuciosos sobre a ordem dos cabeçalhos. Enviar o cabeçalho WWW-Authenticate antes do cabeçalho HTTP/1.0 401 resolve isso por enquanto.

A partir do PHP 4.3.0, para evitar que alguém escreva um script que revele a senha para uma página que foi autenticada através de um mecanismo externo tradicional, as variáveis PHP_AUTH não serão configuradas se autenticação externa estiver disponível para aquela página em particular e se safe mode estiver ligado. No entanto, REMOTE_USER pode ser usada para identificar um usuário autenticado externamente. Então, você pode usar $_SERVER['REMOTE_USER'].

Nota: Nota de Configuração PHP usa a presença de uma diretiva chamada AuthType para determinar se autenticação externa está em efeito.

Perceba, no entanto, que a diretiva citada não previne que alguém que controle uma URL não autenticada de roubar senhas de URLs autenticadas no mesmo servidor.

Tanto o Netscape Navigator quanto o Internet Explorer limparão o cache de autenticação da janela do navegador para o realm após receber uma resposta 401 do servidor. Isso pode efetivamente "deslogar" um usuário, forçando o mesmo a re-entrar seu nome de usuário e senha. Algumas pessoas usam isso para delimitar o tempo de um login, ou fazer um botão de "log-out".

Example#3 Exemplo de Autenticação via HTTP forçando um(a) novo(a) nome/senha

<<?php
  
function authenticate() {
    
header('WWW-Authenticate: Basic realm="Test Authentication System"');
    
header('HTTP/1.0 401 Unauthorized');
    echo 
"Você deve usar um login e uma senha válidos para acessar esse recurso\n";
    exit;
  }

  if (!isset(
$_SERVER['PHP_AUTH_USER']) ||
      (
$_POST['SeenBefore'] == && $_POST['OldAuth'] == $_SERVER['PHP_AUTH_USER'])) {
   
authenticate();
  } else {
   echo 
"<p>Bem vindo: {$_SERVER['PHP_AUTH_USER']}<br />";
   echo 
"Login antigo: {$_REQUEST['OldAuth']}";
   echo 
"<form action='{$_SERVER['PHP_SELF']}' METHOD='post'>\n";
   echo 
"<input type='hidden' name='SeenBefore' value='1' />\n";
   echo 
"<input type='hidden' name='OldAuth' value='{$_SERVER['PHP_AUTH_USER']}' />\n";
   echo 
"<input type='submit' value='Re Autenticar' />\n";
   echo 
"</form></p>\n";
  }
?>

Esse comportamento não é necessário pelo padrão autenticação básica via HTTP, então você nunca deve depender disso. Testando com Lynx mostrou que Lynx não limpa as credenciais de autenticação com uma resposta 401 do servidor, então apertar voltar e depois avançar novamente abrirá o recurso enquanto os requerimentos de credenciais não mudarem. Porém, o usuário pode apertar a tecla '_' para limpar a sua informação de autenticação.

Também perceba que até o PHP 4.3.3, Autenticação HTTP não funcionava usando o servidor IIS da Microsoft com a versão CGI do PHP devido a limitação do IIS. Para fazer ela funcionar no PHP 4.3.3 ou superior, você deve editar a configuração "Directory Security" do IIs. Clique em "Editar" e apenas marque "Anonymous Access", todos os outros campos devem ser mantidos desmarcados.

Outra limitação é se você estiver usando o módulo IIS (ISAPI), você não deve usar as variáveis PHP_AUTH_* mas sim, a variável HTTP_AUTHORIZATION. Por exemplo, considere o seguinte código: list($user, $pw) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));

Nota: IIS Note: Para autenticação via HTTP funcionar no IIS, a diretiva do PHP cgi.rfc2616_headers deve estar configurada para 0 (o valor padrão).

Nota: Se modo de segurança estiver ligado, o uid do script é adicionado a parte realm do cabeçalho WWW-Authenticate.