Funcionamiento de la autenticación básica en un navegador

De entre los diferentes sistemas de autenticación que podemos utilizar en una aplicación web, la autenticación básica, HTTP Basic, es la más utilizada habitualmente, debido principalmente a su sencillez y al amplio soporte de que dispone desde hace mucho tiempo en todo tipo de navegadores, aunque como veremos, adolece de importantes problemas de seguridad que no la hacen recomendable en muchas situaciones.

En las siguientes líneas voy a explicar el funcionamiento de este método de autenticación y doy un ejemplo sobre cómo se podría implementar directamente con apache, o mediante una aplicación programada en PHP.

Funcionamiento

Este sistema de autenticación está diseñado para permitir a un navegador o programa, aportar unas credenciales basadas en nombre de usuario y contraseña, que le permitan autenticarse ante un determinado servicio. El sistema como veremos es muy sencillo de implementar, pero sin embargo no está pensado para ser utilizado sobre líneas públicas, debido a que las credenciales que se envían desde el cliente al servidor, aunque no se envían directamente en texto plano, se envían únicamente codificadas en base-64, lo que hace que se puedan obtener fácilmente.

Veamos cómo funciona paso a paso este sistema:

  1. El cliente solicita una página al servidor, normalmente tras acceder a una url mediante un enlace o escribiéndola directamente en el navegador. La página que se ha solicitado requiere autenticación y el cliente no ha proporcionado todavía ninguna información, únicamente ha dicho que quiere acceder a ese recurso.
    GET /privado/index.php HTTP/1.1
    Host: example.com
  2. El servidor responde con un error 401 y comienza el proceso de autenticación
    HTTP/1.1 401 Unauthorised
    Server: example.com/1.1
    Date: Thu, 17 Oct 2006 19:18:15 GMT
    WWW-Authenticate: Basic realm="example.com"
    Content-Type: text/html
    Content-Length: 311
     
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
     "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
    <HTML>
      <HEAD>
        <TITLE>Error</TITLE>
        <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
      </HEAD>
      <BODY><H1>401 Unauthorised.</H1></BODY>
    </HTML>
  3. El cliente recibe la cabecera de error 401 y, normalmente, muestra un diálogo al usuario pidiéndole su nombre de usuario y contraseña.
  4. Una vez introducidos los datos de usuario y contraseña, el cliente envía la información al servidor. Para ello vuelve a repetir la petición inicial, pero enviando esta vez una cabecera extra con la información de autenticación. Esta información se generará haciendo codificando en base64 el string “<>:<>”, así por ejemplo, si se está autenticando el usuario “Aladdin”, con contraseña “open sesame”, se realizaría la codificación base64 de “Aladdin:open sesame”, que corresponde con la cadena QWxhZGRpbjpvcGVuIHNlc2FtZQ==
    GET /privado/index.php HTTP/1.1
    Host: example.com
    Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
  5. El servidor recibe la nueva petición de página con la información de autenticación, si la información suministrada es válida, se permite el acceso al recurso, y en caso contrario, se vuelve a responder con un código de error 401, para que el usuario vuelva a introducir su información. En el caso de que la autenticación haya sido positiva, en las siguientes peticiones de página se enviará automáticamente la cabecera con la autenticación, para evitar que el diálogo que pide el nombre de usuario y la contraseña aparezca contínuamente.

Implementación en Apache

Implementar este tipo de autenticación sobre un recurso definido en apache es realmente sencillo, necesitaremos únicamente utilizar el comando htpasswd para crear un archivo de usuarios y contraseñas, y crear un archivo .htaccess en el recurso que queramos proteger.

Lo primero por tanto es crear el archivo que va a almacenar los usuarios y contraseñas. Indicaremos el nombre del archivo, que suele ser habitualmente .htpasswd, mediante el parámetro -c. En el mismo comando de creación del archivo podemos indicar un nombre de usuario “Aladdin” para el que queremos proporcionar una contraseña.

bash# htpasswd -c .htpasswd Aladdin
New password: 
Re-type new password: 
Adding password for user Aladdin

El comando creará el archivo y nos solicitará la introducción por duplicado de la contraseña del nuevo usuario. Si miramos el contenido del archivo podremos observar como hay una línea para el usuario Aladdin, y que tras su nombre, y separado por dos puntos, aparece encriptada la contraseña que hemos proporcionado.

bash# more .htpasswd 
Aladdin:L6kRPn034nnOw

Pese a estar la contraseña encriptada, puede ser posible obtenerla mediante ataques de fuerza bruta, por lo que deberemos asegurarnos en dejar el archivo .htpasswd con los permisos apropiados y, por supuesto, fuera de las carpetas accesibles vía web.

Una vez creado el archivo con las contraseñas es hora de crear el archivo que va a controlar el acceso a nuestro recurso privado. Para ello crearemos un archivo de nombre .htaccess dentro de la carpeta, y copiaremos el siguiente texto:

AuthUserFile /www/example.com/users/.htpasswd
AuthName "Example.com"
AuthType Basic
require valid-user

Lo que se indica en el archivo creado, es que vamos a utilizar una autenticación de tipo básica AuthType Basic, que cualquier usuario autenticado podrá acceder require valid-user, que el nombre del recurso es Example.com AuthName "Example.com", y que el archivo con los usuarios y contraseñas está en /www/example.com/users/.htpasswd.

Con esto ya tendríamos el recurso protegido mediante autenticación.

Implementación mediante PHP

Como hemos visto en la descripción del funcionamiento del sistema, la autenticación funciona en base a una serie de cabeceras que envían tanto el servidor como el cliente. En el punto anterior hemos visto como implementar la seguridad mediante un mecanismo que viene por defecto en apache, pero podemos también crear una autenticación propia basada en PHP en cualquier aplicación nuestra, enviando las cabeceras correspondientes al cliente.

Para ello, todos aquellos programas PHP que queramos que estén bajo autenticación deberán compartir una porción de código que compruebe si se han recibido credenciales de identificación, y si son correctas, enviando la correspondiente cabecera 401 en caso contrario, y permitiendo el acceso al resto del programa si son válidas.

El siguiente fragmento de código hace todo esto, he incluido comentarios para explicar cada paso.

 
// el nombre del recurso sobre el que se le pedirá autenticación al cliente
$realm = 'Example.com';
 
// Defino la lista de usuarios, aunque normalmente estará en un 
// archivo externo o base de datos
$users = array('Aladdin' => 'open sesame','admin' => 'admpwd');
 
if (!isset($_SERVER['PHP_AUTH_USER'])) {
   header('WWW-Authenticate: Basic realm="'.$realm.'"');
   header('HTTP/1.1 401 Unauthorized');
   echo 'Texto a mostrar si el usuario pulsa cancelar...';
   exit;
} else {
   $user = $_SERVER['PHP_AUTH_USER'];
   $pwd  = $_SERVER['PHP_AUTH_PW'];
 
   if($users[$user] != $pwd){
      header('WWW-Authenticate: Basic realm="'.$realm.'"');
      header('HTTP/1.1 401 Unauthorized');
      echo 'Texto a mostrar si el usuario pulsa cancelar...';
      exit;   
   }
}
 
// Si hemos llegado aquí significa que el usuario ha proporcionado
// credenciales y que éstas son correctas

De todas formas hay que tener en cuenta que esto es únicamente un ejemplo de utilización, y que como se indica en el código, normalmente los usuarios residirán en algún tipo de archivo externo o base de datos. Habitualmente este código también residirá en algún include que pueda ser reutilizado entre diferentes programas que requieran de la misma autenticación, de hecho, hay diversas clases que disponen de esta lógica ya programada y que nos permiten simplificar su utilización.

Twitter Digg Delicious Stumbleupon Technorati Facebook Email

9 Respuestas para “Funcionamiento de la autenticación básica en un navegador”

  1. hola estoy intentando proteger una carpeta de mi web con el fichero .htaccess y .htpasswd.
    Mi problema es que cuando intento validarme no me hace caso y me lo vuelve a pedir.
    He copiado el texto tal y como lo poneis vosotros cambiando la ruta del .htpasswd y tambien he creado un usuario y password copiando lo que habeis puesto pero nada.
    sabeis porque puede ser??

    gracias

  2. Hola Javier, el problema más frecuente suele ser que la ruta indicada en AuthUserFile no coincida con la ruta correcta de un archivo .htpasswd

  3. Un articulo muy interesante y un blog bastante chulo.
    Desde ahora nos veremos con frecuencia. 😉 Un saludo.
    Josecarlos.
    diseño web – diseño paginas web

  4. Duda:
    Si quisiera un programa que se autenticara a una direccion llamada:
    http://www.desarrolloX.com
    ¿Cómo tendria que poner la linea que en el post se escribe como:

    GET /privado/index.php HTTP/1.1
    Host: example.com

    He intentado
    GET http://www.desarrrolloX/ipunto/index.php HTTP/1.1
    GET /desarrrolloX/ipunto/index.php HTTP/1.1
    GET desarrrolloX/ipunto/index.php HTTP/1.1

    Ninguno funcional
    Vale!

  5. Humberto, depende del lenguaje de programación que utilices. Las líneas del post que comentas explican cómo funciona el protocolo, luego cada lenguaje suele tener sus librerías que te permiten abstraerte y utilizar unos métodos que encapsulan esos mensajes.

  6. Hola Patxi. Muchas gracias por responder.
    Uso PHP y he usado lineas de codigo como esta para enviar html crudo:

    header(‘header(‘GET /privado/index.php HTTP/1.1’);
    header(‘Host: example.com’);

    Mi duda es si /privado/index.php es una ruta en mi servidor o la url en donde me quiero autenticar. La misma duda la tengo para “example.com”

    Vale!

  7. Hola Humberto, creo que estás mezclando dos cosas.

    El fragmento de código PHP que pongo en el post sirve para que una página pida al usuario login y contraseña y verifique los datos vía http basic.

    Si lo que quieres hacer es que tu programa PHP se autentique en otra página, tienes que establecer una comunicación http entre tu programa y esa página, indicando primero el dominio y luego la ruta y las credenciales. Para ello tienes varias opciones, puedes hacerlo tú a mano con la función fsockopen (tienes mucha información en php.net/fsockopen), o podrías utilizar alguna librería que te abstraiga de tener que enviar los mensajes http como, curl (http://es.php.net/curl) o alguna clase php, busca en google: php http client.

    Espero que te sirva

  8. Patxi: Muchas gracias. Con tu ayuda resolvi un problema que ya me llevaba casi una semana. Pongo la solucion por si llegara ser de utilidad a alguien. En la linea 3 y 4 Pongo las dos lineas de las que venimos hablando desde que te comence a pedir ayuda. En la primer linea uso el comando que me sugeriste (fsockopen).

    Por cierto Mi problema era que tengo una pagina de internet donde los usuarios inician sesion con un login y un password. Por otro lado, los usuarios tenian asignados ciertos archivos en el disco duro. Esa seccion estaba protegida con un .htaccess y para accesar el usuario tenia que volver a introducir su password y contrasena.

    Muchas Gracias Patxi!

    1

  9. Ha sido muy sensillo implementarlo mediante PHP, este es el codigo que he utilizado:

    ‘manzanita’,’admin’ => ‘admpwd’);

    if (!isset($_SERVER[‘PHP_AUTH_USER’])) {
    header(‘WWW-Authenticate: Basic realm=”‘.$realm.'”‘);
    header(‘HTTP/1.1 401 Unauthorized’);
    echo ‘No estas Autorizado a entrar a este sitio!!’;
    exit;
    } else {
    $user = $_SERVER[‘PHP_AUTH_USER’];
    $pwd = $_SERVER[‘PHP_AUTH_PW’];

    if(array_key_exists($user,$users)){
    if($users[$user] != $pwd){
    echo ‘Tu Contrasena no es Valida!’;
    header(‘WWW-Authenticate: Basic realm=”‘.$realm.'”‘);
    header(‘HTTP/1.1 401 Unauthorized’);
    exit;
    }
    } else {
    echo ‘Tu Usuario no Existe!’;
    header(‘WWW-Authenticate: Basic realm=”‘.$realm.'”‘);
    header(‘HTTP/1.1 401 Unauthorized’);
    exit;
    }
    }

    echo “Bienvenido seas “.$user;
    ?>