viernes, 12 de marzo de 2010

Enlace y Autenticación PHP con LDAP (LDAPhp.class.php)


Es un clásico escuchar los intentos de realizar este enlace (bind) y que no funcione porque no sabemos como configurar la cadena de conexión del usuario enlace o el usuario a autenticar. A esta cadena de conexión la llamamos Distinguished Name/Nombre distinguido y un ejemplo de DN es:

DN: cn=Andres Perez Albela, dc=empresa, dc=com

Siendo su RDN (Relative Distinguished Name/Nombre Distinguido Relativo) la siguiente:

RDN: cn=Andres Perez Albela

Esto quiere decir que el usuario es Andres Perez Albela y que el dominio es empresa.com, y me parece bueno resaltar que el Directorio Activo (AD) separa mediante la variable DC (Domain Controller//Controlador de Dominio) cada parte del dominio explotada por el símbolo "." Es decir, si hacemos explode('.', "empresa.com.pe") obtendremos tres partes en el arreglo, y cada parte del arreglo es un DC, siendo en este caso algo así:

DC=empresa, DC=com, DC=pe

Ahora podrán ver un ejemplo de Active Directory que hice para poder explicarles el enlace.


Imagen de Estructura de Active Directory


Como podemos ver, el dominio es: labserver.com y las unidades organizacionales (OU) son:

-Casa
--Conexiones
--Cuarto 1
--Cuarto 2

Siendo Casa el OU Principal y Conexiones, Cuarto 1, Cuarto 2 las sub-unidades organizacionales de Casa (Ojo que no dejan de ser OU).


Podemos apreciar el "samAccountName" de un usuario ingresando a esa opción


Dentro de Cuarto 1 tenemos un usuario con los siguientes datos:

CN : Andres Perez Albela H.
samAccountName : aperezalbela

La historia es que el desarrollador de un sistema creado en PHP quiere autenticar a sus usuarios con LDAP. Entonces crea una clase para realizar el enlace, pero este enlace no funcionará si no se crea un usuario enlace en el Directorio Activo, que en este caso es PHP y se encuentra en CASA/Conexiones, entonces hay que crear una cadena de conexión para ese usuario:

CN=PHP,OU=Conexiones,OU=Casa,DC=labserver,DC=com

Luego de haber autenticado al usuario PHP, se procede a buscar al usuario aperezalbela en el AD, entonces tenemos que crear una cadena para ubicarnos en la OU principal y poder hacer búsquedas con ldap_search que busca en la OU principal y bajo ella (sub-OU). La cadena para este caso es:

OU=Casa, DC=labserver, DC=com

De esta manera al desarrollador solo le quedará crear un filtro de búsqueda para comprobar la existencia del usuario aperezalbela. El filtro es:

(&(objectClass=user)(samAccountName=$usuario))

Como podemos ver, tenemos la variable $usuario que es el usuario a buscar y que recibiéndola por un formulario y habiéndose interpretado por php pues quedaría así:

(&(objectClass=user)(samAccountName=aperezalbela))

Para finalmente intentar realizar el enlace (autenticación de ese usuario en LDAP) con la contraseña enviada por el usuario en el formulario web.

Ahora presento el código PHP para la autenticación (LDAPhp.php).

<?php

//Importamos el manejador LDAP
require_once 'LDAPhp.class.php';

//Datos del Usuario a Autentificar con tu aplicacion en PHP
//La mayoría de veces estos datos no son una constante, sino valores recibidos por $_POST[]
$usuario = "aperezalbela";
$clave = "prueba";

//Datos de Configuración del Servidor
$servidor="192.168.1.2";
$puerto="389";

//Datos de Configuración para la conexión al LDAP (DN == "Distinguished Name")
$cadenaConexionUsuario = "CN=PHP,OU=Conexiones,OU=Casa,DC=labserver,DC=com";

//Datos de Configuración de usuario enlace LDAP-PHP, en este caso PHP (CN=PHP)
$password = "pruebaLdap";

//Datos de Configuración para el enlace al LDAP (DN == "Distinguished Name")
//En este caso nos situamos en el OU (Unidad Organizacional) principal
$dn = "OU=Casa, DC=labserver, DC=com";

//Datos de Configuración de Filtro para la búsqueda de un Usuario
$filtro = "(&(objectClass=user)(samAccountName=$usuario))";

//Conectamos al servidor LDAP con los => "Datos de Configuración del Servidor"
$PHPLdap = new PHPLdap($servidor, $puerto);

//Luego de habernos conectado al servidor, enlazamos la conexion identificandonos con un password
//para el usuario PHP (CN=PHP)
$PHPLdap->enlazarPHPLdap($cadenaConexionUsuario, $password);

//Luedo de haber realizado el enlace principal con el usuario PHP (CN=PHP) creado para este fin
//Procedemos a realizar la búsqueda del usuario a autenticar con la aplicación en PHP.
//PD: No se envía el usuario como parámetro ya que se encuentra interpretado en la variable: $filtro
$autenticar = $PHPLdap->autenticarUsuario($dn, $filtro, $clave);

//Verificamos el valor de retorno de la autenticación
if($autenticar===true){
//Autenticado
//Aca es donde generas la $_SESSION, COOKIES, o como manejes la autenticación
//Luego redirecciónas, a la página privada, etc.
echo "- A buena hora! Lograste autentificar al usuario $usuario";
}
else{
//Acceso Denegado
echo "- Contraseña Incorrecta para el usuario $usuario";
}

//Cerramos la conexion a LDAP
$PHPLdap->cerrarConexion();
?>

Y también el código de la clase (LDAPhp.class.php)

/*** Título: Enlace PHP con LDAP para autenticar usuarios en aplicaciónes web
**** Autor: Andrés Pérez-Albela H.** Web: http://www.seguridadinformatica.org/
**** Blog: http://develsec.blogspot.com
**** Versión: 1.0
**** Lenguaje: PHP
**** Licencia: Copyright © SeguridadInformatica.Org
**** Creative Commons Atribución-Sin Obras Derivadas 3.0 Unported License
****/

class PHPLdap{
private $ds;
private $bind;

public function PHPLdap($host, $puerto){

if(!isset($this->ds)){
if($this->ds = (ldap_connect($host, $puerto))){
echo "- LDAP conectado a: ".$host."";
}
else
{
die("- No me puedo conectar con servidor LDAP");
}
}
}

public function enlazarPHPLdap($cadenaConexionUsuario, $password){
$this->bind = @ldap_bind($this->ds, $cadenaConexionUsuario, $password);

if($this->bind){
echo "- Se realizó el enlace principal satisfactoriamente con el usuario PHP";
}
else
{
die("- El enlace principal no se pudo llevar a cabo");
}
}

public function autenticarUsuario($dn, $filtro, $clave){
$busqueda=ldap_search($this->ds, $dn, $filtro);
$resultados = ldap_get_entries($this->ds, $busqueda);

//Habiendo buscado el usuario a autenticar con "ldap_search" y obteniendo resultados en
//un arreglo $resultados, con "ldap_get_entries", pasamos a condicionar la existencia
//de resultados positivos.

if($resultados["count"]>0){
//Ingreso a la condicional, verificando con el "count" que el usuario exista
//Quiere decir que si encontró al usuario
//Luego obtenemos el DN (Nombre Distinguido) del usuario

$dnUsuario = trim($resultados[0]["distinguishedname"][0]);

//Para poder finalmente realizar el enlace final, siendo "enlace final" el login correcto.
//Si y solo si... la clave es correcta

if(@ldap_bind($this->ds, $dnUsuario, $clave)){
//Se realiza el bind, siendo la clave correcta, y se retorna un valor VERDADERO (true)
return true;
}
else
{
return false;
}
}
}

public function cerrarConexion(){
//Cerramos la conexion a LDAP
ldap_close($this->ds);
}
}
?>

Bueno, y eso es todo. Espero que les sirva la información, el post es completamente propio y cualquier consulta no duden en comentarla acá en el post.

Saludos, hasta la próxima entrada.

jueves, 11 de marzo de 2010

Bienvenidos a DevelSec

Bueno, creo que ya era hora de crear mi propio blog para poder expresar mis ideas y exponer las cosas que sé y aprendo.

Bienvenidos a todos los interesados en estos dos rubros en los que enfocaré el contenido del blog: Seguridad Informática y Programación.

Espero poder ayudar a las personas que me escriban/comenten y agradeceré sugerencias/recomendaciones para que este blog siga su trayecto y continúe con vida.

Así que con este post comienza el Blog DevelSec.

Saludos entonces para mis lectores!