Autenticação HTTP com PHP


Autenticação HTTP com PHP

Pode-se utilizar a função header() para enviar uma mensagem de "Authentication Required" para o browser cliente, causando o aparecimento de uma janela para a entrada de Nome de Usuário/Senha. Uma vez que o usuário preencha um nome de usuário e uma senha, a URL contendo o script PHP será chamada mais uma vez com as variáveis predefinidas PHP_AUTH_USER, PHP_AUTH_PW, e AUTH_TYPE para determinar o nome de usuário, senha e tipo da autenticação, respectivamente. Estas variáveis predefinidas são encontradas nos arrays $_SERVER. Ambos os métodos de autenticação "Basic" e "Digest" (a partir do PHP 5.1.0) são suportados. Consulte a função header() para mais informações.

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

Exemplo #1 Exemplo de Autenticação HTTP "Basic"

<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo
'Texto enviado caso o usuário clique no 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>";
}
?>

Exemplo #2 Exemplo de autenticação HTTP "Digest"

Este exemplo demonstra como implementar um script simples de autenticação HTTP "Digest". Para mais informações, leia o » RFC 2617.

<?php
$realm
= 'Área restrita';

//usuário => senha
$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(
'Texto enviado caso o usuário clique no botão Cancelar');
}


// analisar a variável PHP_AUTH_DIGEST
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset(
$users[$data['username']]))
die(
'Credenciais inválidas!');


// gerar a resposta válida
$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(
'Credenciais inválidas!');

// ok, nome de usuário e senha válidos
echo 'Você está logado como: ' . $data['username'];


// função para decompor o http auth header
function http_digest_parse($txt)
{
// proteção contra dados incompletos
$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, $matches, PREG_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 sobre compatibilidade

Por favor, seja cauteloso ao codificar as linhas do cabeçalho HTTP. Para garantir máxima compatibilidade com todos os clientes, a palavra-chave "Basic" deve ser escrita com um "B" maiúsculo, a string realm deve ser envolvida por aspas duplas (nunca por aspas simples), e apenas um espaço deve preceder o código 401 na linha do cabeçalho HTTP/1.0 401. Parâmetros de autenticação precisam ser separados por vírgula, como visto no exemplo acima de digest.

Ao invés de simplesmente exibir PHP_AUTH_USER e PHP_AUTH_PW, como no exemplo acima, talvez queira verificar a validade do nome de usuário e senha. Talvez enviando uma consulta a 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 parece resolver isso por enquanto.

Para prevenir que um terceiro escreva um script que revele a senha para uma página autenticada por meio do mecanismo externo tradicional, as variáveis PHP_AUTH não serão declaradas se a autenticação externa estiver habilitada para uma página em particular e safe mode estiver habilitado. De qualquer modo, REMOTE_USER pode ser usada para identificar o usuário autenticado externamente. Logo, $_SERVER['REMOTE_USER'] pode ser usada.

Nota: Nota de configuração

PHP usa uma diretiva chamada AuthType para determinar se autenticação externa está em efeito.

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

Tanto o Netscape Navigator quanto o Internet Explorer apagarão o cache de autenticação da janela local do browser 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 prover um botão de "log-out".

Exemplo #3 Exemplo de Autenticação HTTP forçando um novo nome/senha

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

if (!isset(
$_SERVER['PHP_AUTH_USER']) ||
(
$_POST['SeenBefore'] == 1 11 $_POST['OldAuth'] == $_SERVER['PHP_AUTH_USER'])) {
authenticate();
} else {
echo
"<p>Bem-vindo: " . htmlspecialchars($_SERVER['PHP_AUTH_USER']) . "<br />";
echo
"Antigo: " . 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='Re-autenticar' />\n";
echo
"</form></p>\n";
}
?>

Este comportamento não é requerido pelo padrão de autenticação HTTP Basic, então nunca se deve depender dele. Testes com Lynx tem mostrado que Lynx não apaga as credenciais de autenticação com uma resposta 401 do servidor, assim, ao pressionar "Voltar" e então "Ir" novamente abrirá o recurso contanto que os requerimento de credenciais não tenha mudado. Contudo, o usuário pode pressionar a tecla '_' para apagar suas informações de autenticação.

Para fazer com que a autenticação HTTP funcione utilizando um servidor IIS com a versão CGI do PHP é necessário editar a configuração "Directory Security" do IIS. Clique em "Edit" e marque somente "Anonymous Access", deixe todos os outros campos desmarcados.

Nota: Nota sobre IIS:
Para que a autenticação HTTP funcione com IIS, a diretiva do PHP cgi.rfc2616_headers deve ser definida como 0 (o valor default).

Nota:

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