Sep 19

Introducción.

A diario utilizamos el protocolo SSH para establecer conexiones seguras entre servidores y acceder a shells remotos, montar sistemas de archivos remotos y crear túneles entre muchas otras facilidades.  Habitualmente nos autenticamos ante el servidor remoto utilizando contraseñas, es decir, para indicarle quienes somos le enviamos nuestro nombre de usuario y para garantizarle al servidor que efectivamente somos quienes decirmos ser, el enviamos nuestra contraseña, la cual es secreta y sólo el servidor y nosotros la conocemos.

Este es el método de autenticación mas utilizado y viene por defecto con la instalación del servicio SSH, sin embargo no es muy práctico cuando es necesario interactuar en múltiples ocasiones con múltiples servidores ya que será necesario ingresar la contraseña para autenticarnos múltiples veces.  Una solución a este problema es el uso de llaves (privada y pública) para autenticarnos con el servidor remoto.

El procedimiento es simple, el cliente crea su propio par de llaves y le envía al servidor remoto su llave pública.  En el servidor se agrega esta llave recibida a la lista de llaves autorizadas para su conexión.

La próxima vez que el usuario (que cuenta con sus llaves) intenta acceder al servidor a su cuenta (que cuenta con su llave pública autorizada), este último utilizará el método basado en llaves para autenticarlo con el cual no será necesario que ingrese su contraseña nuevamente.

En este punto es necesario hacer una aclaración, la idea fundamental es la de evitar la necesidad de especificar la contraseña del usuario frecuentemente, sin embargo la llave privada del usuario viene por defecto encriptada con una passphrase, la cual debe especificarse cada vez que el usuario la va a utilizar.

La diferencia de uso entre la contraseña y la passphrase es que esta última puede ser recordada mediante agentes SSH los cuales almacenan las passphrases la primera vez que se utilizan y la proveen automáticamente en las veces subsecuentes, es decir, en una sesión de trabajo sólo será necesario escribir una única vez la passphrase específica de una llave para la autenticación basada en estas.

Existen distintos agentes SSH como ssh-agent (incluído con OpenSSH), GNUPG Agent que tiene emulación para OpenSSH, keychain y GNOME Keyring entre otros.  En mi caso utilizo GNOME así que utilizo el Keyring que viene con él por defecto.

Configuración.

Crear las llaves en el cliente.

En el cliente se inicia creando la pareja de llaves, pública y privada.

$ ssh-keygen -b 1024 -t dsa

Generating public/private dsa key pair.
Enter file in which to save the key (/home/jimezam/.ssh/id_dsa):   [1]
Enter passphrase (empty for no passphrase):                        [2]
Enter same passphrase again:                                       [3]
Your identification has been saved in /home/jimezam/.ssh/id_dsa.
Your public key has been saved in /home/jimezam/.ssh/id_dsa.pub.
The key fingerprint is:
55:32:22:01:1f:b3:5c:f1:44:28:49:55:93:55:f9:1f jimezam@micliente
The key's randomart image is:
+--[ DSA 1024]----+
|        ..*=*=+++|
|         o.Ooo.=.|
|          =  .=  |
|         .     . |
|        S       .|
|                E|
|                .|
|                 |
|                 |
+-----------------+

Se recomienda utilizar la ubicación sugerida por defecto [1] para el almacenamiento de las llaves.  En este punto [2] debe especificarse el passphrase (concepto que se introdujo en la sección anterior) para cifrar la llave privada, es necesario que recuerde muy bien la cadena de texto que utilizó ya que debe ser utilizada durante su confirmación [3] y una vez por sesión cada vez que vaya a realizar la autenticación basada en llaves con otro servidor.

Terminado este procedimiento se deberá tener dos archivos nuevos con las llaves recién generadas: id_dsa.pub es la llave pública y id_dsa es la llave privada.

$ file ~/.ssh/id_dsa.pub

id_dsa.pub: OpenSSH DSA public key

$ file ~/.ssh/id_dsa

id_dsa: PEM DSA private key

Transmitir la llave pública al servidor.

El siguiente paso es el de transmitir la llave pública del usuario en el cliente al servidor.

$ scp ~/.ssh/id_dsa.pub jimezam@miservidor:

Autorizar en el servidor la llave pública enviada desde el cliente.

Se autoriza la llave pública enviada desde el cliente para que el servidor confíe en ella la próxima vez que el primero intente autenticarse.  Para esto se realiza una conexión remotoa con el servidor.

$ ssh jimezam@miservidor

Ya en el servidor se ejecutan los siguientes comandos.

$ mkdir ~/.ssh

$ chmod go-w ~/.ssh

$ cat ~/id_dsa.pub >> ~/.ssh/authorized_keys

$ rm ~/id_dsa.pub

$ chmod 600 ~/.ssh/authorized_keys

Utilización.

La próxima vez que el usuario (con las llaves) intenta acceder a través de SSH al servidor (que confía en la llave pública) el agente SSH que esté utilizando (en mi caso GNOME) le solicitará una única vez el passphrase de la llave privada [2] y de allí en adelante autenticará de manera transparente las demás sesiones que abra el usuario.

$ ssh jimezam@miservidor

GNOME Keyring solicitando una passphrase

Enlaces.

para que confíe en ella la próxima vez que el primero intente autenticarse.
Tagged with:



En September 19 de 2010, Jorge Iván Meza Martínez escribió acerca de Autenticación basada en llaves para SSH.
Aug 11

Problema.

Condor no inicia en un nodo específico.  En sus logs se encuentran mensajes como los siguientes.

CollectorLog

08/11 21:59:35 AUTHENTICATE: handshake failed!
08/11 21:59:35 ERROR: SECMAN:2004:Failed to create security session to <192.168.1.230:9180> with TCP.|AUTHENTICATE:1002:Failure performing handshake|AUTHENTICATE:1004:Failed to authenticate using PASSWORD

MasterLog

08/11 22:00:36 DC_AUTHENTICATE: authenticate failed: AUTHENTICATE:1002:Failure performing handshake|AUTHENTICATE:1004:Failed to authenticate using PASSWORD
08/11 22:00:36 condor_write(): Socket closed when trying to write 297 bytes to <192.168.1.230:45838>, fd is 10
08/11 22:00:36 Buf::write(): condor_write() failed
08/11 22:00:36 SECMAN: Error sending response classad!
08/11 22:00:36 error: SEC_PASSWORD_FILE must be owned by Condor’s real uid

Explicación.

El archivo donde se almacena la contraseña del cluster para la autenticación basada en las mismas, tiene un propietario o unos permisos incorrectos.

Solución.

Identificar la ubicación del archivo de contraseñas.

# condor_config_val SEC_PASSWORD_FILE

/etc/condor/condor_credential

Verificar sus permisos y propietario.

# ls -l /etc/condor/condor_credential

-rw-r–r– 1 condor root 256 Aug 11 21:47 /etc/condor/condor_credential

Corregir sus permisos y propietario.

# chown root:root /etc/condor/condor_credential

# chmod 600 /etc/condor/condor_credential

# ls -l /etc/condor/condor_credential

-rw——- 1 root root 256 Aug 11 21:47 /etc/condor/condor_credential

Tagged with:



En August 11 de 2010, Jorge Iván Meza Martínez escribió acerca de SEC_PASSWORD_FILE must be owned by Condor’s real uid.
Jun 07

Introducción.

En el presente artículo se especifica la configuración del servicio necsario para asegurar los nodos bajo una infraestructura basada en llaves SSH la cual permitirá al usuario root las siguientes acciones.

  • Acceso SSH desde el nodo de adminstración y cada uno de los demás nodos del cluster.
  • Acceso SSH entre todos los nodos cabeza del cluster.
  • Acceso SSH entre los nodos cabeza y los nodos de trabajo del cluster.

Iniciar los nodos del cluster.

Para realizar las siguientes tareas de configuración es necesario garantizar que todos los nodos del cluster: cabeza y trabajadores, se encuentren activos.  Para hacer esto desde el servidor de máquinas virtuales ejecute los siguientes comandos.

# virsh start c-head

# virsh start c-wn1

# virsh start c-wn2

Configuración general del servicio.

Los pasos de configuración descritos a continuación se deberán realizar en c-head y desde allí serán propagados a los distintos nodos trabajadores.

Realizar una copia de seguridad de la configuración actual del servicio.

# mv /etc/ssh/sshd_config /etc/ssh/sshd_config.orig

Crear el nuevo archivo de configuración del servicio.

# vi /etc/ssh/sshd_config

Protocol 2

# No passwords
PermitRootLogin without-password
## PasswordAuthentication no
PermitEmptyPasswords no
UsePAM no

# Pub keys configuration
RSAAuthentication yes
DSAAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
KerberosAuthentication no
GSSAPIAuthentication no
GSSAPICleanupCredentials no

# Hostbased and pubkey yes
PubkeyAuthentication yes
ChallengeResponseAuthentication no
X11Forwarding no

# Hostbased
HostbasedAuthentication yes
IgnoreRhosts no

#OPTIONALLY DON’T CHECK gethostbyaddr(), TRUST CLIENT
#HostbasedUsesNameFromPacketOnly yes

# chmod 600 /etc/ssh/sshd_config

Propagar el nuevo archivo de configuración a los nodos trabajadores.

# scp /etc/ssh/sshd_config jimezam@c-wn1:/tmp

# scp /etc/ssh/sshd_config jimezam@c-wn2:/tmp

En cada uno de los nodos, c-wn1 y c-wn2, ubique apropiadamente el archivo de configuración recién copiado ejecutando los siguientes comandos.

# mv /tmp/sshd_config /etc/ssh/sshd_config

# chown root:root /etc/ssh/sshd_config

# chmod 400 /etc/ssh/sshd_config

Configurar la autenticación basada en el huésped para root.

En el nodo cabeza (c-head) ejecutar los siguientes comandos.

# cd /root

# ssh-keyscan -t rsa,dsa c-head c-head.jorgeivanmeza.com c-nfs c-nfs.jorgeivanmeza.com c-wn1 c-wn1.jorgeivanmeza.com c-wn2 c-wn2.jorgeivanmeza.com > ssh_known_hosts

# mv ssh_known_hosts /etc/ssh/ssh_known_hosts

# chown root:root /etc/ssh/ssh_known_hosts

# chmod 644 /etc/ssh/ssh_known_hosts

Propagar el archivo ssh_known_hosts a los nodos trabajadores.

# scp /etc/ssh/ssh_known_hosts jimezam@c-wn1:/tmp/ssh_known_hosts

# scp /etc/ssh/ssh_known_hosts jimezam@c-wn2:/tmp/ssh_known_hosts

En cada uno de los nodos, c-wn1 y c-wn2, ubique apropiadamente el archivo de huéspedes conocidos recién copiado ejecutando los siguientes comandos.

# mv /tmp/ssh_known_hosts /etc/ssh/ssh_known_hosts

# chown root:root /etc/ssh/ssh_known_hosts

# chmod 644 /etc/ssh/ssh_known_hosts

Creación del archivo de .shosts.

El archivo .shosts corresponde al listado de huéspedes contenidos en ssh_known_hosts sin sus respectivas llaves.  Este archivo también deberá ser replicado en los demás nodos del cluster.

En el nodo cabeza (c-head) ejecutar los siguientes comandos.

# cut -d’ ‘ -f1 < /etc/ssh/ssh_known_hosts|sort -u | grep -v , > shosts

# mv shosts /root/.shosts

# chown root:root /root/.shosts

# chmod 600 /root/.shosts

Propagar el archivo .shosts a los nodos trabajadores.

# scp /root/.shosts jimezam@c-wn1:/tmp/.shosts

# scp /root/.shosts jimezam@c-wn2:/tmp/.shosts

En cada uno de los nodos, c-wn1 y c-wn2, ubique apropiadamente el archivo de huéspedes confiables recién copiado ejecutando los siguientes comandos.

# mv /tmp/.shosts /root/.shosts

# chown root:root /root/.shosts

# chmod 600 /root/.shosts

Agregar nuevos nodos al cluster.

Si se agregan nuevos nodos al cluster es necesario que estos se conviertan en huéspedes conocidos.  Para hacer esto es necesario actualizar el archivo manipulado en el punto anterior con sus nuevas llaves y propagado a todos y cada uno de los demás nodos.

Para obtener las llaves que deberán concatenarse con las ya existentes se deberá ejecutar el siguiente comando con una lista de los hostnames de los nuevos nodos separados por espacio.

# ssh-keyscan -t rsa,dsa nodo1 nodo2 nodo3 >> /etc/ssh/ssh_known_hosts

De igual manera será necesario que se actualice el archivo .shosts de todos los nodos del cluster de la manera como se describe en el apartado anterior.

Actualizar la configuración de los clientes.

Es necesario indicarle al cliente SSH que confíe en el método de autenticación basado en los huéspedes recién configurado.  Para hacer esto es necesario realizar el siguiente paso en cada uno de los nodos del cluster.

# vi /etc/ssh/ssh_config

HostbasedAuthentication   yes
EnableSSHKeysign          yes

# /etc/init.d/sshd restart

Verificar la autenticación basada en el huésped para root.

Si la configuración de la autenticación basada en el huésped fue exitosa, el usuario root podrá iniciar una sesión SSH en cualquier nodo desde cualquier otro sin necesidad de autenticarse nuevamente con una contraseña.

Por ejemplo, desde el c-wn1 se deberá poder abrir sesiones a c-wn2 y c-head sin que se requiera una contraseña para el usuario root.

# hostname

c-wn1.jorgeivanmeza.com

# ssh root@c-head

Last login: Mon Jun  7 20:44:42 2010 from c-wn1

# hostname

c-head.jorgeivanmeza.com

Configurar la autenticación basada llaves públicas para usuarios sin privilegios.

Esta estrategia permitirá que los usuarios convencionales (no root) puedan acceder a sus respectivas cuentas entre nodos sin necesidad de una nueva autenticación por contraseña.  Inicialmente será necesario replicar los archivos con las llaves públicas de cada uno de los usuarios pero posteriormente este proceso se simplificará ya que el directorio de las cuentas de  usuario (/home) estará compartido por NFS y sólo será necesario que se genere el archivo authorized_keys para cada uno de los archivos que requieran de esta facilidad.

Iniciar sesión con el usuario sin privilegios al cual se le va a configurar la autenticación basada en llave pública.  Por ejemplo: jimezam.  Con el usuario elegido y en cada uno de los nodos crear el directorio .ssh de la siguiente manera.

$ mkdir ~/.ssh/ && cd ~/.ssh/

$ chmod 700 ~/.ssh

Desde uno de los nodos, c-head por ejemplo, ejecutar los comandos descritos a continuación.

$ ssh-keygen -t dsa

Generating public/private dsa key pair.
Enter file in which to save the key (/home/jimezam/.ssh/id_dsa):
Enter passphrase (empty for no passphrase): Romeo
Enter same passphrase again:
Romeo
Your identification has been saved in /home/jimezam/.ssh/id_dsa.
Your public key has been saved in /home/jimezam/.ssh/id_dsa.pub.
The key fingerprint is:
98:ef:03:e3:8d:89:2e:7d:d5:b6:2b:5d:a6:11:3d:37 jimezam@c-head.jorgeivanmeza.com

$ cat id_dsa.pub >> authorized_keys

$ chmod 600 authorized_keys

$ chmod 600 id_dsa

Replicar la llave pública del usuario en los demás nodos del cluster.  Este paso será obviado cuando el directorio de cuentas de usuario sea único al estar compartido por NFS.

$ scp authorized_keys jimezam@c-wn1:/home/jimezam/.ssh/

$ scp authorized_keys jimezam@c-wn2:/home/jimezam/.ssh/

$ scp id_dsa jimezam@c-wn1:/home/jimezam/.ssh/

$ scp id_dsa jimezam@c-wn2:/home/jimezam/.ssh/

Verificar la autenticación basada llave primaria para usuarios sin privilegios.

Si la configuración de la autenticación basada en la llave primaria fue exitosa, el usuario sin privilegios podrá iniciar una sesión SSH en cualquier nodo desde cualquier otro sin necesidad de autenticarse nuevamente con una contraseña, sin embargo deberá proporcionar la frase secreta (passphrase) con la que se encriptó su llave durante el proceso de creación (ssh-keygen).

Por ejemplo, desde el c-wn1 se deberá poder abrir sesiones a c-wn2 y c-head sin que se requiera una contraseña para el usuario jimezam.

$ hostname

c-wn1.jorgeivanmeza.com

$ ssh jimezam@c-head

Enter passphrase for key ‘/home/jimezam/.ssh/id_dsa‘: Romeo

Last login: Mon Jun  7 21:19:21 2010 from c-wn2

$ hostname

c-head.jorgeivanmeza.com

Utilizar ssh-agent para recordar las frases secretas.

Es posible minimizar el número de veces que se hace necesario introducir las frases secretas para acceder a las cuentas de un usuario en diferentes nodos según los procedimientos descritos antes en este artículo.  Esto se logra utilizando la aplicación ssh-agent que se encarga de memorizar las frases secretas durante la sesión del usuario así que estas sólo serán solicitadas una vez por sesión.

El aplicativo puede accederse directamente desde la línea de comando o pueden utilizarse versiones GUI mas elaboradas como el GNOME KeyRing que es la versión de este software adaptada al escritorio de GNOME.

Para utilizarlo es necesario que el shell que se está utilizando esté cubierto por el ssh-agent, lo cual puede hacerse de la siguiente manera.

$ ssh-agent /bin/bash

Posteriormente ejecute el comando ssh-add e ingrese las frases secretas que requiera.

$ ssh-add

Enter passphrase for /home/jimezam/.ssh/id_dsa: Romeo
Identity added: /home/jimezam/.ssh/id_dsa (/home/jimezam/.ssh/id_dsa)

A partir de este momento y mientras dure la sesión, la llave podrá ser utilizada sin la necesidad de ingresar manualmente la frase secreta para desencriptarla.

Solución de problemas.

Al intentar ejecutar el comando ssh-add es posible que reciba el siguiente mensaje de error.

Could not open a connection to your authentication agent.

Esto significa que el shell que se está utilizando no fue abierto bajo el ssh-agent y por lo tanto no conoce cual es su proceso.  Para solucionar esto de manera temporal puede ejecutar una de las siguientes dos soluciones.

$ eval `ssh-agent`

o reiniciar el shell bajo el agente.

$ exec ssh-agent bash

Enlaces.

Tagged with:



En June 7 de 2010, Jorge Iván Meza Martínez escribió acerca de Cluster-C: Configuración del servicio SSH entre los nodos.
May 10

Introducción.

La situación es la siguiente, se cuenta con un CMS desarrollado en PHP bajo el framework de CodeIgniter con su propio sistema de autenticación en el que se almacenan los usuarios de la siguiente manera.

CREATE TABLE IF NOT EXISTS `core_usuario` (
`id_usuario` int(11) unsigned NOT NULL auto_increment,
`estado` enum(‘activo’,'inactivo’) NOT NULL,
`_username` varbinary(16) NOT NULL,
`_password` varbinary(32) NOT NULL,
`nombres` varchar(50) NOT NULL,
`apellidos` varchar(50) NOT NULL,
`fecha_nacimiento` date default NULL,
`tipo_documento` enum(‘cc’,'ce’,'ti’,'nit’) NOT NULL,
`documento_identidad` varchar(12) NOT NULL,
`genero` enum(‘m’,'f’) NOT NULL,
`correo` varchar(255) NOT NULL,
`pagina` varchar(255) default NULL,
`observaciones` varchar(255) default NULL,
`fecha_creacion` datetime NOT NULL,
`fecha_actualizacion` datetime NOT NULL,
PRIMARY KEY  (`id_usuario`),
);

En el sitio se ha instalado un foro basado en la versión 1.1.11 de Simple Machines el cual comparte la base de datos con el CMS.  Se necesita encontrar la forma de unificar los nombres de los usuarios y las contraseñas para tener una única identificación para la autenticación en los dos sistemas.

Para hacer esto se utiliza el API de SSI de SMF de la siguiente manera.

Hooks de integración.

SMF provee una facilidad para alterar el ciclo de flujo del software mediante la manipulación de hooks que se agregan a etapas específicas del programa para modificar su comportamiento por defecto.  En este caso nos interesa crear un hook sobre el punto integrate_validate_login ya que ese es el punto exacto en que SMF va a realizar la validación de los usuarios durante su autenticación.

Para implementar esto se crea un archivo llamado jimhook.php (el nombre es arbitrario) con el siguiente contenido inicial.

<?php
define('SMF_INTEGRATION_SETTINGS', serialize(array(
    'integrate_validate_login' => 'user_validate',
)));

require_once('SSI.php');

function user_validate()
{
    // Implementación de la lógica de la nueva autenticación.
}
?>

Estos hooks deben asociarse al sistema de foros agregando la siguiente línea al inicio del archivo index.php.

include_once("jimhook.php");

Conexión a la base de datos.

Como se mencionó inicialmente el CMS fue desarrollado en CodeIngniter y en este caso, comparte la base de datos con los foros.  El primer paso es obtener la información de conexión a la base de datos desde los archivos de configuración del CMS.

define('BASEPATH', 1);
include('../pt/system/application/config/database.php');

Teniendo esta información se realiza una conexión global a la base de datos utilizando las funciones de PDO.

$pdo = null;

try
{
    $pdo = new PDO("mysql:host={$db['default']['hostname']};dbname={$db['default']['database']}",
                   $db['default']['username'],
                   $db['default']['password'],
                   array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")
    );
}
catch (PDOException $e)
{
    print "Error!: " . $e -> getMessage() . "<br/>";
    die();
}

Funciones de apoyo del CMS.

Se crean algunas funciones básicas que servirán de soporte para el nuevo procedimiento de autenticación.

function existeUsuarioEnPlataforma($usuario)
{
    global $pdo;

    $stmt = $pdo -> prepare("SELECT id_usuario FROM `usuarios` WHERE user=?");

    $results = $stmt -> execute(array($usuario));

    return ($stmt -> rowCount() > 0);
}

Verifica si el usuario identificado por el nombre de usuario ($usuario) existe o no registrado en el CMS.  Retorna true o false según se encuentren o no efectivamente registrados.

function autenticarUsuarioEnPlataforma($usuario, $contrasena)
{
    global $pdo;

    $stmt = $pdo -> prepare("SELECT id_usuario FROM `usuarios` WHERE user=? AND pass=?");

    $results = $stmt -> execute(array($usuario, MD5($contrasena)));

    return ($stmt -> rowCount() > 0);
}

Verifica que el usuario identificado con el nombre de usuario ($usuario) tenga efectivamente a $contraseña como clave de usuario.  Retorna true en caso de coincidir, false de lo contrario.

Nótese como el CMS almacena sus contraseñas en MD5 motivo por el cual es necesario realizar esta conversión antes de realizar la comparación en la consulta SQL.

Funciones de apoyo de los foros.

De manera análoga, estas funciones facilitan la manipulación de la información en las tablas del sistema de foros, las cuales tienen configurado el prefijo foros_ en sus tablas.

function existeUsuarioEnForos($usuario)
{
    global $pdo;

    $stmt = $pdo -> prepare("SELECT ID_MEMBER FROM foros_members WHERE memberName = ?");

    $results = $stmt -> execute(array($usuario));

    if($stmt -> rowCount() == 0)
        return false;

    $fuente = $stmt -> fetch();

    return $fuente['ID_MEMBER'];
}

Verifica si el usuario identificado por el nombre de usuario ($usuario) existe o no registrado en el foro .  Retorna el identificador del usuario si existe o false de lo contrario.

function traerUsuario($usuario, $contrasena)
{
    global $pdo, $sourcedir, $modSettings;

    if(!existeUsuarioEnPlataforma($usuario))
        return false;

    $stmt = $pdo -> prepare("SELECT * FROM `usuarios` WHERE user=?");

    $results = $stmt -> execute(array($usuario));

    $fuente = $stmt -> fetch();

    require_once($sourcedir . '/Subs-Members.php');

    $regOptions = array('username' => $fuente['user'],
                        'password' => $contrasena,
                        'password_check' => $contrasena,
                        'email' => $fuente['email'],
                        'posts' => '0',
                        'ID_GROUP' => '0',
                        'ID_POST_GROUP' => '4');

    $memberID = registerMember($regOptions);

    return ($memberID > 0);
}

Obtiene la información del usuario de plataforma identificado por $usuario y $contrasena, y lo agrega en la base de datos del foro a través de su API interno.  A este nivel no se realiza ninguna verificación de autenticación.

function activarUsuarioForo($usuario, $estado='1')
{
    global $pdo;

    $stmt = $pdo -> prepare("UPDATE foros_members SET is_activated = ? WHERE memberName = ?");

    $control = $stmt -> execute(array($estado, $usuario));

    return $control;
}

Activa al usuario del foro recién creado, el cual por defecto siempre queda en espera de confirmación (is_activated = 3).

function actualizarContrasenaForo($usuario, $contrasena)
{
    global $pdo;

    $passwd       = sha1(strtolower($usuario) . $contrasena);
    $passwordSalt = substr(md5(mt_rand()), 0, 4);

    $stmt = $pdo -> prepare("UPDATE foros_members SET passwd = ?, passwordSalt = ? WHERE memberName = ?");

    $control = $stmt -> execute(array($passwd, $passwordSalt, $usuario));

    return $control;
}

Actualiza la $contrasena del $usuario en el sistema de foros.  Nótese como la $contrasena se recibe de manera plana y se almacena en la base de datos de SMF en SHA1.

Acerca del comportamiento del login de SMF.

Algo que se debe tener en cuenta acerca del comportamiento del login de SMF es que desde el cliente (navegador del usuario) se envía la contraseña ya en su propia representación SHA1 así que si la contraseña con que se va a comparar no está en la misma representación, como en este caso, el CMS la almacena en MD5, se tiene entonces un problema.

Para solventar esto, el sistema de foros permite solicitar nuevamente la contraseña al usuario (retry) y en este segundo intento la envía totalmente plana para su manipulación libre.  En este punto es donde la aprovechamos para verificar la autenticación del usuario contra la información del CMS, crear el usuario en el foro si no existe y actualizar la contraseña en el foro previniendo que haya sido actualizada antes en el CMS.

Implementación del nuevo proceso de autenticación.

function user_validate()
{
    $user = $_REQUEST['user'];
    $id   = 0;

    // Verificar un usuario proveniente de plataforma.

    if(existeUsuarioEnPlataforma($user))
    {
        $id = existeUsuarioEnForos($user);

        // Nueva lógica de autenticación.
    }

    // El usuario no existe en plataforma, verificar los nativos del foro.

    return false;
}

La base de la nueva autenticación es entonces la definición de la función user_validate que corresponde con la especificada inicialmente durante la configuración de los hooks.

En ella se verifica inicialmente si el usuario que intenta acceder al sistema (login) se encuentra presente o no en la base de datos de usuarios del CMS.  En caso de no estar presente el control de la autenticación se libera para que el sistema verifique sus usuarios propios, esto permite definir también usuarios internos del foro que no estén presentes en el CMS.

El paso siguiente consiste en determinar si el usuario existe previamente o no en el foro, es decir, no es su primer ingreso al mismo.  A partir de esta respuesta se toma uno de los siguientes caminos.

  • Si no está disponible la contraseña plana (primer login) se solicita nuevamente (segundo login).
  • Si el usuario no existe en el foro entonces ...
    • Se verifica que el nombre de usuario y la contraseña coincidan.
    • Se crea el usuario en el foro.
    • Se activa al nuevo usuario.
  • Si el usuario ya existía previamente en el foro entonces ...
    • Se verifica que el nombre de usuario y la contraseña coincidan.
    • Se actualiza la contraseña en el foro por si ha sido actualizada previamente en el CMS.

La implementación de estas acciones se detalla a continuación.  En el caso en que el usuario no exista previamente en el foro se ejecuta la siguiente lógica del negocio.

if($id === false)
{
    // Esta disponible la contrasena (2do. login).

    if(isset($_REQUEST['passwrd']) && !empty($_REQUEST['passwrd']))
    {
        $auth = autenticarUsuarioEnPlataforma($user, $_REQUEST['passwrd']);

        if($auth)                           // La informacion del usuario es valida.
        {
            $id = traerUsuario($user, $_REQUEST['passwrd']);

            activarUsuarioForo($user);

            return false;                   // La autenticacion tiene EXITO.
        }
        else                                // La informacion del usuario NO es valida.
            return true;                    // La autenticacion FALLA.
    }
    else                                    // No esta disponible la contrasena (1er. login).
        return "retry";                     // Vuelva a solicitar el login para confirmar.
}

En el caso en que el usuario si exista previamente en el foro, el procedimiento es mas simple y se ejecuta la siguiente lógica del negocio.

// El usuario existe en el foro.

if($id !== false)
{
    // Esta disponible la contrasena (2do. login) -> actualizar la contraseña en el foro.

    if(isset($_REQUEST['passwrd']) && !empty($_REQUEST['passwrd']))
    {
        $auth = autenticarUsuarioEnPlataforma($user, $_REQUEST['passwrd']);

        if($auth)                           // La informacion del usuario es valida.
        {
            actualizarContrasenaForo($user, $_REQUEST['passwrd']);

            return false;                   // La autenticacion tiene EXITO.
        }
        else                                // La informacion del usuario NO es valida.
            return true;                    // La autenticacion FALLA.
    }
    else                                    // No esta disponible la contrasena (1er. login).
        return "retry";                     // Vuelva a solicitar el login para confirmar.
}

Conclusiones.

Hasta ahora, con poca experiencia en su uso, SMF parece ser un sistema de foros útil y práctico.  El proceso de determinar esta unificación de la autenticación fue largo y doloroso debido a la poca documentación que pude encontrar acerca del API, la cual en su mayor parte se encuentra diseminada en los foros de su sitio web.

En términos de su código parece tener un nivel decente de flexibilidad, el concepto de hooks permite realizar modificaciones interesantes al flujo normal del software, sin embargo es difícil formalizarlo al no contar con información suficiente de su lógica de funcionamiento.  Algunas partes del software, en especial los temas, adolecen de separación MVC por lo que se hace necesario editar funciones, entre comillas, que retornan el código HTML que se desea modificar siendo esto molesto, difícil y propenso a errores, sobretodo si se compara con tener archivos PHP separados con la lógica y HTML con la presentación como sería una mejor opción.

Finalmente, después de varios días de luchas y de pruebas, la unificación de la autenticación está funcionando y se ve bien, de todas maneras es mi primer acercamiento a este software por lo que no puedo garantizar que sea la mejor aproximación.  Como siempre, estoy abierto a sugerencias constructivas.

Enlaces.

Tagged with:



En May 10 de 2010, Jorge Iván Meza Martínez escribió acerca de Integración de una autenticación externa con los foros de Simple Machines 1.1.11.
Mar 18

laconica_logoHace poco instale Laconica en mi sitio web para tener un historial completo de las entradas que voy actualizando de lo que estoy haciendo, esto lo hice en mi proveedor de hosting Dreamhost. Hasta el momento todo habia funcionado bien, pero ahora que queria conectar Ping.fm con mi Laconica la cosa no funciono.

Resulta que Laconica implementa el API de Twitter para permitir a aplicaciones de terceros poder puplicar entradas en el microblog, para esto es necesario realizar una validacion del usuario a traves de Autenticacion Basica, la cual es soportada por PHP solamente cuando esta instalado como modulo y no como CGI como esta instalado en Dreamhost.

Asi que si necesita utilizar la verificacion de usuario de Laconica en un sitio basado en Dreamhost no es posible sin cambiar alguna configuracion o incluso cambiar la forma de validacion del usuario.

Enlaces :

Tagged with:



En March 18 de 2009, Marlon J. Manrique escribió acerca de Laconica en Dreamhost.