Autenticación HTTP con PHP
Con la
función header se puede enviar un mensaje de "Autenticación requerida"
al navegador del cliente para mostrar una ventana emergente donde introducir un
usuario y una contraseña. Una vez introducidos estos datos,
el URL que contiene el script de PHP será invocado de nuevo con las
variables predefinidas
PHP_AUTH_USER, PHP_AUTH_PW
y AUTH_TYPE establecidas al nombre de usuario, contraseña y
tipo de autenticación, respectivamente. Estas variables se encuentran en
el array $_SERVER. Se admiten ambos métodos de autenticación,
'Basic' y 'Digest' (desde PHP 5.1.0). Véase la
función header para más información.
Un fragmento de un script de ejemplo que forzaría la autenticación
en una página es el siguiente:
Ejemplo de autenticación HTTP 'Basic'
Hola {$_SERVER['PHP_AUTH_USER']}.";
echo "
Introdujo {$_SERVER['PHP_AUTH_PW']} como su contraseña.
";
}
?>
]]>
Ejemplo de autenticación HTTP 'Digest'
Este ejemplo muestra cómo implementar un sencillo script de
autenticación HTTP 'Digest'. Para más información lea el RFC 2617.
contraseña
$usuarios = array('admin' => 'micontraseña', 'invitado' => 'invitado');
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$dominio.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($dominio).'"');
die('Texto a enviar si el usuario pulsa el botón Cancelar');
}
// Analizar la variable PHP_AUTH_DIGEST
if (!($datos = analizar_http_digest($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($usuarios[$datos['username']]))
die('Credenciales incorrectas');
// Generar una respuesta válida
$A1 = md5($datos['username'] . ':' . $dominio . ':' . $usuarios[$datos['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$datos['uri']);
$respuesta_válida = md5($A1.':'.$datos['nonce'].':'.$datos['nc'].':'.$datos['cnonce'].':'.$datos['qop'].':'.$A2);
if ($datos['response'] != $respuesta_válida)
die('Credenciales incorrectas');
// Todo bien, usuario y contraseña válidos
echo 'Se ha identificado como: ' . $datos['username'];
// Función para analizar la cabecera de autenticación HTTP
function analizar_http_digest($txt)
{
// Protección contra datos ausentes
$partes_necesarias = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
$datos = array();
$claves = implode('|', array_keys($partes_necesarias));
preg_match_all('@(' . $claves . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $coincidencias, PREG_SET_ORDER);
foreach ($coincidencias as $c) {
$datos[$c[1]] = $c[3] ? $c[3] : $c[4];
unset($partes_necesarias[$c[1]]);
}
return $partes_necesarias ? false : $datos;
}
?>
]]>
Sobre la compatibilidad
Hay que tener cuidado al codificar las líneas de cabeceras HTTP. Para garantizar la mayor
compatibilidad con todos los clientes, la palabra 'Basic' debe escribirse con
'B' mayúscula, la cadena del dominio debe estar entre comillas dobles (no simples),
y debería haber exactamente un espacio precediendo al código 401 de la
línea de la cabecera HTTP/1.0 401. Los parámetros de autenticación deben
estar separados por comas, como se vió en el ejemplo de 'Digest' anterior.
En lugar de imprimir simplemente PHP_AUTH_USER
y PHP_AUTH_PW como se hizo en el ejemplo anterior,
podría ser conveniente validar el usuario y la contraseña,
tal vez enviando una consulta a una base de datos o buscando el
usuario en un fichero dbm.
Cuidado con los navegadores Internet Explorer defectuosos. Parecen
ser muy quisquillosos con el orden de las cabeceras. Por ahora,
parece ser que el truco está en enviar la cabecera
WWW-Authenticate antes que la cabecera
HTTP/1.0 401.
Para prevenir que alguien escriba un script que
revele la contraseña de una página que se autenticó con un
mecanismo externo tradicional, las variables PHP_AUTH no se
establecerán si la autenticación externa está habilitada para esa
página en particular y si el &safemode; está habilitado. Independientemente,
se puede emplear REMOTE_USER
para identificar al usuario autenticado externamente. De este modo, se podrá usar
$_SERVER['REMOTE_USER'].
Sobre la configuración
PHP utiliza la presencia de una directiva AuthType para
determinar si una autenticación externa está en uso.
Observe, sin embargo, que lo anterior no impide que alguien que
controle un URL no autenticado pueda robar contraseñas
de URL autenticados en el mismo servidor.
Tanto Netscape Navigator como Internet Explorer borran la caché de autenticación
de la ventana del navegador local para el dominio al recibr una respuesta
401 del servidor. Esto, en efecto, puede hacer que se cierre la sesión de un usuario,
obligándolo a reintroducir su nombre de usuario y contraseña. Algunos utilizan esto para
inicios de sesión «expiradas» o proveer un botón de «Cerrar sesión».
Ejemplo de autenticación HTTP forzando un nuevo usuario/contraseña
Bienvenido: " . htmlspecialchars($_SERVER['PHP_AUTH_USER']) . " ";
echo "Antiguo: " . htmlspecialchars($_REQUEST['UsuarioAntiguo']);
echo "\n";
}
?>
]]>
El estándar de autenticación HTTP Basic no requiere
este funcionamiento, por lo que no se debería depender de ello. Las pruebas con
Lynx han mostrado que Lynx no limpia
las credenciales de autenticación con una respuesta 401 del servidor, por lo que al presionar «atrás»
y luego «adelante» abrirá el recurso nuevamente mientras los requisitos de credenciales
no hayan cambiado. Sin embargo, el usuario puede pulsar la
tecla '_' para limpiar su información de autenticación.
Para que la Autenticación HTTP funcione en un servidor IIS con la versión CGI
de PHP, se debe editar la configuracion de "Seguridad de directorios" de IIS:
hacer clic en "Editar" y solo marcar
"Acceso anónimo"; todos los demas campos
deberían estar sin marcar.
Sobre IIS:
Para que funcione la Autenticación HTTP con IIS, la directiva de PHP
cgi.rfc2616_headers debe
establecerse a 0 (el valor predeterminado).
Si el modo seguro está habilitado, el
uid del script se añade a la parte del dominio de la
cabecera WWW-Authenticate.