Contraseñas seguras en PHP

Guardar las contraseñas de forma segura en base de datos es uno de los procesos más habituales y más importante de cualquier aplicación con usuarios. En PHP es muy sencillo hacerlo, pero antes vamos a explicar cuales son los problemas y las soluciones para generar y guardar contraseñas seguras.

Lo primero y más importante es que nunca debemos guardar en plano las contraseñas en la base de datos. El motivo es que si nuestra base de datos se ve comprometida el nombre de usuario y la contraseña puede ser usado en otras webs ya que la mayoría de la gente los reutiliza. Que levante la mano quien no tenga la misma contraseña en decenas de webs.

Entonces, ¿cómo las guardamos?. Podríamos hacerlo utilizando alguno de los algoritmos de hash más comunes como son md5 o sha1 pero por una parte estos algoritmos llevan rotos desde hace años y por otra se podrían utilizan tablas arcoíris (rainbow tables) que no son ni más ni menos que tablas de contraseñas con el hash ya generado de forma que es mucho más sencillo realizar un ataque de fuerza bruta.

Para evitar este tipo de ataques utilizaremos Salt. Pero ¿qué es Salt? Esta técnica consiste en añadir una cadena aleatoria a la contraseña antes de que se utilice para generar el hash. Pongamos un ejemplo sencillo para que se entienda, digamos que la contraseña es la palabra «hola», lo que vamos a hacer es generar una cadena aleatoria que deberemos guardar en base de datos para poder regenerar el hash de nuevo. Ahora lo que hacemos es por ejemplo concatenar la contraseña con la cadena aleatoria y sobre el resultado de la concatenación generamos el hash. Vendría a ser algo así:

hash(«hola» . «A%$bcJeL»)

La función hash podría ser cualquier función hash segura, el resultado de la cual es lo que guardaremos en base de datos como contraseña y como ya habíamos mencionado también guardaremos el Salt «A%$bcJeL» en el ejemplo para en algún momento poder regenerar el hash y así poder comprobar la contraseña. Para comprobarla lo que haríamos es recuperar el hash y el Salt de base de datos, regenerar el hash con la contraseña que introduzca el usuario en el formulario correspondiente y comparar. Sería algo así:

hash($campoFormulario . $saltDeBaseDeDatos) == $contrasenyaDeBaseDeDatos

Ahora la tabla arcoíris ya no sirve porque los hashes resultantes al tener Salt serán completamente diferentes.

Una vez hemos entendido todo esto ¿cual es la forma correcta de hacerlo en PHP? Pues usar las funciones password_hash y password_verify. La primera genera un hash con Salt de forma automática, nosotros lo único que tenemos que darle es la contraseña en plano y el algoritmo seguro a utilizar para generar el hash. Actualmente se recomienda el algoritmo CRYPT_BLOWFISH pero si le indicamos PASSWORD_DEFAULT como algoritmo entonces la función utilizará el aceptado como más seguro en ese momento de forma que si sale uno mejor será ese el que se empiece a utilizar.

password_hash ( string $password , integer $algo [, array $options ] ) : string

En el ejemplo sería:

password_hash(«hola», PASSWORD_BCRYPT)

Esto nos generar un hash con este formato:


Como veis tenemos todo lo necesario en un único hash. El tipo de algoritmo que en nuestro caso es CRYPT_BLOWFISH, las opciones del algoritmo si las hubiera, el Salt y el hash de la contraseña. Ahora tan solo necesitamos guardar este hash completo en la base de datos en lugar del hash y el Salt por separado.

Si queremos comprobar la contraseña usaremos la función password_verify a la que tan solo tendríamos que pasarle por un lado la contraseña y por otro el hash completo ya que ahí tiene todos los datos necesarios para la comprobación.

password_verify ( string $password , string $hash ) : bool

Esta función internamente hace algo parecido a lo que hacíamos nosotros en el ejemplo de comprobación con Salt devolviéndonos al final verdadero o falso dependiendo de si la contraseña coincide o no.

Con todo esto ya sabéis como generar un hash seguro y como comprobar la validez de vuestras contraseñas a partir del hash. Pero si tienes cualquier duda déjamela en un comentario.

Nos vemos.

Emular terminal desde PHP en Servidores Virtuales Gestionados (caso de Comvive)

Desde que trabajo en Pórtico Legal quería empezar a usar el sistema de control de versiones (yo siempre lo uso y aquí no tenían nada, una locura) y poder usarlo para los despliegues en preproducción y producción como solución intermedia hasta que tengamos algo más avanzado. Hasta la fecha se estaban haciendo mediante ftp con todos los problemas que eso supone, entre ellos los siguientes y sus posibles soluciones:

  • Mientras se ha subido un archivo y se está subiendo otro que depende de este la web se puede romper, lo más probable es que ocurra. Esto no se solventa completamente usando git sobre un único servidor pero se minimiza muchísimo.
  • Nunca se sabe que versión está en producción. En algunos momento se habían subido archivos por diferentes personas y una no sabía lo que había subido la otra.
  • Si se toca algo en el servidor un simple «git status» te lo va a chivar, y un simple «git checkout …» te va a solucionar la papeleta.
  • Se va a poder desplegar nuevas versiones simplemente etiquetando y haciendo un «git checkout tag«

 

Aquí el problema venía por parte del hosting de comvive que al ser un VPS gestionado no te dan cuenta de shell. Para solucionarlo al final utilicé un pequeño script php que ejecutaba la orden que le pasas como parámetro:

<?php

$command = $_GET[«command»];

$res = execute($command, __DIR__ . ‘/..’);
echo «<pre>»;
echo «\nCódigo de salida: » . $res[‘code’];
echo «\n» . $res[‘out’];
echo «\nSalida de error:\n» . $res[‘err’];
echo «</pre>»;

function execute($cmd, $workdir = null) {
if (is_null($workdir)) {
$workdir = __DIR__;
}

$descriptorspec = array(
0 => array(«pipe», «r»), // stdin
1 => array(«pipe», «w»), // stdout
2 => array(«pipe», «w»), // stderr
);

$process = proc_open($cmd, $descriptorspec, $pipes, $workdir, null);

$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);

$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);

return [
‘code’ => proc_close($process),
‘out’ => trim($stdout),
‘err’ => trim($stderr),
];
}

 

La etiqueta pre la utilizo para que la salida se vea bien si la llamamos desde un navegador. Pues bien, una vez subido podemos ejecutar cualquier orden como:

https://dominio/path/script.php?command=orden

Pero como estarás pensando esto no es seguro. Lo que hice yo es meterlo en un directorio y en este meter un .htaccess para que sólo yo tuviera acceso.

Al final lo único que quería era poder usar git, así que me creé un shell script con wget para llamar a esta url. El script es tal que así:

#!/bin/bash

domain=»»

command=»wget -qO – \»http://$domain/commands/command.php?command=git $*\» | sed ‘1d’ | sed ‘\$d'»
echo «Dominio: $domain»
echo «Parámetros: $*»
eval $command

 

Ahora sólo hay que darle permisos de escritura y en mi caso lo he llamado rgit y lo he puesto en el path por comodidad. Esta sería una salida de la orden «rgit status -s«, en este caso un status limpio:

Dominio: www.dominio.com
Parámetros: status -s
Código de salida: 0
Salida de error:

Lo suyo es que el directorio .git con todo el ínidice esté fuera del htdocs del servidor, para ello se puede usar el parámetro –work-tree de git. Si por algún motivo no puede estar fuera podéis utilizar de nuevo un .htaccess para que nadie pueda entrar:

 

deny from all

 

Y con esto he conseguido un rodeo para poder usar una «shell» remota a través de un script PHP. Espero que os sea de ayuda.