30 de abril de 2015

Metodos __get() y __set() PHP

Este es un ejemplo de los métodos mágicos __set() y __get() en una clase con sus respectivas validaciones utilizando el método property_exists
<?php
class Objeto {  
   private $id;  
   private $nombre;  
   private $email;  

   function __construct($id, $nombre, $email) {  
     $this->id = $id;  
     $this->nombre = $nombre;  
     $this->email = $email;  
   }  

   function __clone() {  
     $this->id = ++$this->id;  
   } 
   
   // Esta función comprueba si la propiedad especificada en $var existe en la clase especificada por __CLASS__   

   public function __set($var, $valor) {  
     if (property_exists(__CLASS__, $var)) {  
       $this->$var = $valor;  
     } else {  
       echo "No existe el atributo $var.";  
     }  
   }  

   public function __get($var) {  
     if (property_exists(__CLASS__, $var)) {  
       return $this->$var;  
     }  
     return NULL;  
   }  
 }  

 $obj = new Objeto(1, "objeto1", "prueba1@ejemplo.com");  
 $p = clone $obj;  
 echo $p->id;//2  
 $p->nombre = "nombre cambiado";  
 echo $p->nombre;//nombre cambiado 
?>

Sessiones PHP

Por omisión, PHP usa el gestor interno de almacenamiento files, el cual se establece mediante session.save_handler. Éste guarda los datos de sesión en el servidor en la ubicación especificada por la directiva de configuración session.save_path, pode defecto se encuentra en session.save_path = "c:/wamp/tmp" Para utilizar sesiones mediante el mecanismo propio de PHP (es decir, sin necesidad de crear funciones propias), la directiva session.save_handler del archivo de configuración php.ini debe tener el valor files. [Session] ; Handler used to store/retrieve data. ; http://php.net/session.save-handler session.save_handler = files Para el manejo de las sesiones tenemos un abanico de funciones que nos facilitaran su uso. Todas la variables de sesión son accesibles mediante arrays en la variable global $_SESSION. session_start: inicializa una sesión y crea el valor o identificador de la sesión. session_id: devuelve el valor de la sesión actual. session_regenerate_id: genera un nuevo identificador para la sesión actual. session_unset: limpia todas la variables de sesión registradas. session_destroy: elimina todas la variables de sesión. En PHP, las sesiones se crean mediante la función session_start(). Si la sesión no existía, esta función crea la sesión y le asocia un identificador de sesión único. Si la sesión ya existía, esta función permite que la página tenga acceso a la información vinculada a la sesión.
<?php
session_start();
?>
Como en el caso de las cookies, hay que tener la precaución de utilizar la función session_start() antes de empezar a escribir el contenido de la página, porque si no PHP producirá un aviso y no se creará la sesión. El motivo es que el idetificador de sesión se utiliza en las cabeceras de respuesta HTTP y las cabeceras se envían antes del texto de la página. Es decir, cuando PHP encuentra una instrucción que escribe texto, cierra automáticamente la cabecera; si a continuación PHP encuentra en el programa la función session_start(), da un aviso porque ya se han enviado las cabeceras y no se crea la sesión. El nombre de la sesión se encuentra en php.ini y está especificado a PHPSESSID, este valor se puede cambiar a uno más representativo desde el código teniendo en cuenta que debe especificarse en cada página que inicie sesión ; Name of the session (used as cookie name). ; http://php.net/session.name session.name = PHPSESSID El ejemplo siguiente muestra código incorrecto, ya que utiliza la función session_start() después de haber escrito texto, y el mensaje de aviso generado por PHP.
<?php
// Este código es incorrecto, la cabecera se crea después de crear texto
print "

Hola

\n"; session_start(); ?>
Genera la siguiente salida:

Hola


Warning: session_start(): Cannot send session cache limiter - headers already sent (output started at ejemplo.php:3) in ejemplo.php on line 4
En el fichero .INI hay un apartado dedicado a las sesiones; en el cual se definen ciertos valores que determinarán el comportamiento de tales. - session.use_cookies Indica si se usarán las cookies para almacenar el SID defecto: 1 - session.name Indica el nombre de la cookie que indicará el SID defecto: PHPSESSID - session.auto_start Indica si la sesión se inicializa al empezar el script automáticamente defecto: 0 - session.cookie_lifetime Indica el tiempo de vida de la cookie (si aplicable) Si 0, caduca al cerrar el explorador. defecto: 0 - session.cookie_path Indica el path o los directorios y sub directorios dentro de un sitio web en los que la cookie es válida, el valor por default es / y significa que la cookie es valida para todos los directorios y sub directorios de tu sitio web, pero si se desea que por ejemplo solo sea valida para el subdirectorio /listas dentro de www.ejemplo.com/libros entonces se debe especificar /libros en el sesión.cookie_path - session.cookie_domain Indica el dominio de la cookie para que sea válida, por defecto será el host del dominio en que se creo la sesión. - session.serialize_handler Indica el handler usado para serializar la sesión defecto: php - session.gc_probability Indica el número de veces de cada 100 que se ejecuta el recolector de basura de las sesiones. Éste proceso borra los ficheros de las sesiones que hallan caducado ya. defecto: 1 cada 100 (1%) - session.gc_maxlifetime Indica el número de segundos que deben haber pasado desde la última actualización para que se considere basura. defecto: session.gc_maxlifetime - session.referer_check Indica la cadena que debe contener la variable $HTTP_REFERER (variable que contiene la url que ha referenciado a ésta página (No todos los navegadores envían esta información)) - session.entropy_length Indica el número de bytes que se leerán del fichero - session.entropy_file Indica como crear el fichero defecto: /dev/urandom - session.cache_limiter Determina el tipo de caché HTTP nocache private public defecto: nocache - session.cache_expire Indica el tiempo en minutos para que caduque el documento defecto: 180 - session.use_trans_sid Indica si se utilizará el transid, que modificará automáticamente los enlaces por GET. defecto: 1 Crear variables de Session Cuando una página ha creado una sesión o ha accedido a una sesión ya existente mediante session_start(), la página tiene acceso a la matriz $_SESSION que contiene las variables de esa sesión, los cuales se mantienen y son accesibles desde cualquier página que inicie sesión. Si deseamos crear variables de sesión para que se puedan utilizar a través de diferentes páginas inicializamos el manejo de sesiones con la función session_start y luego guardamos el dato deseado como variable de sesión utilizando la variable global $_SESSION.
<?php
// home.php
session_start();
if (!isset($_SESSION["pais"])) {
    $_SESSION["country"] = "El Salvador"; 
}
?>
El ejemplo siguiente muestra dos páginas. La primera página guarda información en $_SESSION y la segunda la utiliza.
<?php
//Pagina1.php
session_start();
$_SESSION["nombre"] = "Pepito Conejo";
print "

Se ha guardado su nombre.

\n"; ?>
<?php
//pagina2.php
session_start();
print "

Su nombre es $_SESSION[nombre].

\n"; ?>
Los nombres de los índices de la matriz $_SESSION tienen que cumplir las mismas reglas que los nombres de las variables, es decir, que el primer carácter debe ser una letra o un guión bajo (_). Eliminando Datos de Sesion Los valores de $_SESSION se borran como en cualquier otra matriz mediante la función unset(). Uso de la función session_destroy para cerrar la sesión en PHP
<?php
  session_start();
  unset($_SESSION["nombre_usuario"]); 
  unset($_SESSION["nombre_cliente"]);
  session_destroy();
  header("Location: index.php");
  exit;
?>
Un detalle importante que distingue a las sesiones de las cookies es que: en el caso de las cookies, cuando una página pide al navegador que cree una cookie, el valor de la cookie no está disponible en $_COOKIE en esa página. Lo estará en páginas posteriores, cuando el navegador pida otra página y envíe el valor en la petición. en el caso de las sesiones, cuando una página crea un valor en $_SESSION, ese valor está disponible en esa misma página. El usuario puede destruir la sesión cerrando el navegador, pero las sesiones también se pueden destruir por programa mediante la función session_destroy(). Tipos de cookies. Cookies de sesión - son archivos de cookies temporales, que se borran cuando cierras el navegador. Cuando reinicias el navegador y vuelves al sitio que creó la cookie, la página web no te reconocerá. Tendrás que volver a iniciar sesión (si es necesario hacerlo) o seleccionar tus preferencias y temas de nuevo si el sitio utiliza esas funciones. Una cookie de sesión nueva se generará y almacenará tu información de navegación, permaneciendo activa hasta que abandones la página y cierres el navegador. Más sobre las cookies de sesión. Cookies permanentes estos archivos permanecen en una subcarpeta de tu navegador hasta que las eliminas manualmente o tu navegador las elimina de acuerdo con el período de duración establecido en el archivo de la cookie permanente más sobre cookies permanentes. session_set_cookie_params session_set_cookie_params — Establecer los parámetros de la cookie de sesión Establece los parámetros de la cookie definidos en el archivo php.ini. El efecto de esta función sólo se mantiene para la duración del script. Por lo tanto, se necesita llamar a session_set_cookie_params() por cada petición y antes de llamar a session_start(). Esta función actualiza los valores ini en tiempo de ejecución de las claves de configuración ini de PHP que pueden ser recuperadas con la función ini_get(). lifetime Tiempo de vida de la cookie de sesión, definido en segundos. path Ruta en el dominio donde la cookie trabajará. Use una barras simple ('/') para todas las rutas en el dominio. domain Dominio de la cookie, por ejemplo 'www.php.net'. Para hacer las cookies visibles en todos los sub-dominios, el dominio debe ser prefijado con un punto, como '.php.net'. secure Si es TRUE la cookie sólo será enviada sobre conexiones seguras. httponly Si es TRUE PHP intentará enviar la bandera httponly cuando se establezca la cookie de sesión. session_get_cookie_params session_get_cookie_params — Obtener los parámetros de la cookie de sesión Descripción array session_get_cookie_params ( void ) Obtiene los parámetros de la cookie de sesión. Valores devueltos Devuelve una matriz con la información de la cookie de sesión actual, la matriz contiene los siguientes elementos: "lifetime" - El tiempo de vida de la cookie en segundos. "path" - La ruta donde se almacena la información. "domain" - El dominio de la cookie. "secure" - La cookie debería ser enviada solamente sobre conexiones seguras. "httponly" - La cookie sólo puede ser accedida a través del protocolo HTTP.
<?php
function sec_session_start()
{
        $session_name = 'secure_session_main'; // Set a custom session name
        $secure = false; // Set to true if using https.
        $httponly = true; // This stops javascript being able to access the session id.
        ini_set('session.use_only_cookies', 1); // Forces sessions to only use cookies.
        $cookieParams = session_get_cookie_params(); // Gets current cookies params.
        session_set_cookie_params($cookieParams["lifetime"], $cookieParams["path"],          $cookieParams["domain"], $secure, $httponly);  
        session_name($session_name); // Sets the session name to the one set above.
        session_start(); // Start the php session
        session_regenerate_id(TRUE); // regenerated the session, delete the old one.
}
?>

Session Hijackin PHP

Session Hijacking (secuestro o robo de sesión) se refiere a que un individuo (atacante) consigue el identificador de sesión entre una página web y un usuario, de forma que puede hacerse pasar por el usuario y acceder a su cuenta en esa página web. A continuación veremos algunas de las formas en las que un atacante puede robar este identificador y técnicas para intentar prevenirlo. ATAQUE POR FUERZA BRUTA El ataque por fuerza bruta significa probar identificadores aleatoriamente hasta encontrar uno que esté siendo usado. Es como intentar abrir una caja fuerte sin saber la combinación, poniendo números al azar. Como en el caso de la caja fuerte, cuantos más números tenga la combinación (en este caso el identificador de sesión), más difícil será de adivinar. También ayuda el hecho de que el número o identificador sea aleatorio, y no algo que se pueda predecir. El sistema de identificadores de sesión de PHP es aceptable en este sentido. ROBO POR SNIFFING Este tipo de ataque se da cuando el atacante tiene un programa de sniffing en la red del usuario y puede interceptar el tráfico destinado al mismo, incluido su identificador de sesión. Es algo que ha dado mucho de que hablar a causa de Firesheep, una extensión para Firefox que permite robar las sesiones de Facebook, Twitter y otras páginas web muy conocidas en redes inalámbricas públicas. Lo solución ideal sería el uso del protocolo HTTPS que encriptaría toda nuestra conexión, pero si esto no es posible, podemos crear una serie de comprobaciones en las que intentemos asegurarnos de la real identidad de cada usuario. PROPAGACIÓN EN URL Si el identificador de sesión se propaga utilizando la URL en lugar de las cookies, cualquier atacante puede robarlo desde muchos sitios: Un enlace que el propio usuario ponga en un lugar público. Los usuarios típicos no saben para que sirve ese identificador y no le dan importancia. El historial del navegador. El referrer, que es un encabezado que envían muchos navegadores a las páginas web en el que les indican la URL de la que vienen. La forma de prevenir esto es no utilizar la URL para el identificador de sesión; sino mas bien asegurarse que el sid del usuario viaje a través de cookies y únicamente cookies. En PHP esto se consigue con la instrucción:
<?php
ini_set('session.use_trans_sid',0);            // no utilizar URL para sid
ini_set('session.use_only_cookies',1);     // utilizar cookies para enviar sid
?>
tambien de ser possible especificar set session.cookie_secure a true, lasdos recomendaciones anteriores pueden trabajarse desde session_set_cookie_params. ROBO EN SERVIDOR COMPARTIDO Si tenemos nuestra página web alojada en un servidor compartido, los archivos físicos de las sesiones se guardan, por defecto, en un directorio común para todas las páginas web del servidor. Esto quiere decir que todas las personas que tengan su página web en ese mismo servidor, tienen acceso a todos los archivos de sesiones. Dado que el nombre de los archivos es "sess_" más el identificador de sesión, cualquier atacante tendrá una lista de identificadores de sesión válidos con sólo leer la lista de archivos del directorio común. Esta vulnerabilidad se puede combatir de dos formas: Usando la función session_save_path para guardar los archivos de sesión de la página web en un directorio dentro de su cuenta al que sólo pueda acceder PHP (ya sea por estar fuera del directorio web o con un .htaccess con la instrucción deny from all). Este método no es demasiado fiable, ya que el resto de usuarios seguirá pudiendo leer en ese directorio, sólo tienen que averiguar su localización. Guardando las sesiones en base de datos en lugar de en archivos. Esto se consigue fácilmente usando la función session_set_save_handler. Esta solución es la más segura ya que la página web será la única que tendrá acceso a la base de datos y, por tanto, a las sesiones. ROBO POR CROSS-SITE SCRIPTING Si la página web es vulnerable a XSS el atacante puede insertar un código javascript que envíe las cookies de un usuario a su cuenta. Este tipo de ataque se puede prevenir (además de evitando los ataques XSS) haciendo que las cookies de sesión tengan el atributo HttpOnly, que evita que puedan ser manejadas por javascript en la mayoría de navegadores. En PHP esto se consigue con la instrucción:
<?php
ini_set('session.cookie_httponly', 1);
?>
MÉTODOS DE PREVENCIÓN GENERALES Ten cuidado donde guardas la informacion de la sesión del usuario, por omisión, PHP usa el gestor interno de almacenamiento files, el cual se establece mediante session.save_handler. Éste guarda los datos de sesión en el servidor en la ubicación especificada por la directiva de configuración session.save_path, pode defecto se encuentra en session.save_path = "c:/wamp/tmp" Revisa tu php.ini y define un identificador de hash para la función HASH(), aunque recuerda que también puedes especificar el método en código ; Select a hash function for use in generating session ids. ; Possible Values ; 0 (MD5 128 bits) ; 1 (SHA-1 160 bits) ; This option may also be set to the name of any hash function supported by ; the hash extension. A list of available hashes is returned by the hash_algos() ; function. ; http://php.net/session.hash-function session.hash_function = 0 Además de los métodos de prevención concretos vistos para cada tipo de ataque, vamos a ver algunas técnicas de prevención que ayudan a evitar el robo de las sesiones: Descomenta y define un fuerte hash en php.ini, un nivel 5 es suficiente ; session.hash_bits_per_character ; Default Value: 4 ; Development Value: 5 ; Production Value: 5 Utiliza entropy para generar tus SID, desde php.ini establece un valor distinto para session.entropy_length y descomenta el session.entropy_file ; How many bytes to read from the file. ; http://php.net/session.entropy-length session.entropy_length = 0 ; Specified here to create the session id. ; http://php.net/session.entropy-file ; Defaults to /dev/urandom ; On systems that don't have /dev/urandom but do have /dev/arandom, this will default to /dev/arandom ; If neither are found at compile time, the default is no entropy file. ; On windows, setting the entropy_length setting will activate the ; Windows random source (using the CryptoAPI) ;session.entropy_file = /dev/urandom Verifica que tus datos de sesión no se guarden en cache, la información a la que se refiere es a los headers que se envía en cada petición ; Set to {nocache,private,public,} to determine HTTP caching aspects ; or leave this empty to avoid sending anti-caching headers. ; http://php.net/session.cache-limiter session.cache_limiter = nocache Limitar tiempo de inactividad: session_cache_expire: Devuelve la caducidad de la caché actual, La caducidad de la caché se reinicia al valor predeterminado de 180 minutos almacenado en session.cache_expire en el momento de iniciar una petición. Por lo tanto, se necesita llamar a session_cache_expire() por cada petición (y antes de llamar a session_start()),la información a la que se refiere es a los headers que se envía en cada petición desde php.ini puedes establecer un tiempo prudente ; Document expires after n minutes. ; http://php.net/session.cache-expire session.cache_expire = 180 En el php.ini esta un atributo llamado 'session.cookie_lifetime', si está en 0 va a durar hasta que el navegador se cierre tambien puedes indicar cuanto debe durar la cookie de sesion, en esta línea session.gc_maxlifetime = 1440 que esta dado en segundos, cambias el 1440 por el número de segundos que estimes conveniente. Si no tienes acceso al php.ini puedes utilizar session_set_cookie_params con su parámetro respectivo que es lifetime en este caso se termina sesión cuando el usuario cierra el explorador web:
<?php
session_set_cookie_params(0);
session_start();
?>
Para que funcione debes eliminar debidamente las cookies de session cuando un usuario termine session. Este script maneja valida el tiempo establecido de vida para una sesión, en caso de que la validación haya expirado destruye la sesión y redirige al usuario al área de login para que vuelva a registrarse , el siguiente código debe colocarse en la página de inicio de sesión
<?php
session_start(); 
$_SESSION['tiempo']=time();  
?>
Este script deberá colocarse en toda página que requiera una sesión de usuario autorizada
<?php
session_start(); 
include('archivo.php'); 
..... 
..... 
?>
Y este será el contenido de archivo.php que se encargara de cerrar una sesión si es que esta ha expirado de acuerdo a un tiempo establecido por nosotros a conveniencia
<?php
$time = 3600; // una hora en mili-segundos 

// verificamos si existe la sesión 
// el nombre "session_name" es como ejemplo 
if(isset($_SESSION["session_name"])) 
{ 
      // verificamos si existe la sesión que se encarga del tiempo 
      // si existe, y el tiempo es mayor que una hora, expiramos la sesión  
      if(isset($_SESSION["expire"]) && time() > $_SESSION["expire"] + $time) 
      { 
           echo'';  
       // también puedes utilizar header(“Location:index.php”);
         
     // destruir informacion de session en el server
 
          unset($_SESSION["expire"]); 
          unset($_SESSION["session_name"]); 
         session_unset();
         session_destroy(); 

// destruir informacion de session en el cliente 
$session_cookie_params = session_get_cookie_params(); 
setcookie(session_name(), '', time() - 24 * 3600, $session_cookie_params['path'], $session_cookie_params['domain'], $session_cookie_params['secure'], $session_cookie_params['httponly']); 

// Limpiar el array $_SESSION
$_SESSION = array();  

      } 
      // creamos o redefinimos el valor de la variable de session 
      else 
      { 
           $_SESSION["expire"] = time(); 
       } 
} 
?>
Cambiar el identificador de sesión. Cada cierto tiempo o después de cada acción, cambiar el identificador de la sesión por otro distinto y eliminar la sesión antigua, aparte se debe generar nuevos IDs de sesión para distintos clientes. El programa del lado del servidor nunca debería usar IDs entregados por el browser para un usuario logueado, esto es, una vez que el usuario se loguea en el sistema, éste debería generar automáticamente un nuevo ID de sesión y utilizar éste en el futuro. Desde el código que realiza el login debería utilizarse sesion_regenerate_id() a fin de no utilizar el que por defecto entrega PHP
<?php
if(($_POST['user'] == "demasiadovivo") && ($_POST['pass'] == "123456"))  
{  
    session_regenerate_id(true);  
    $_SESSION['user'] = "demasiadovivo";      
}  
?>
La función session_regenerate_id() crea un nuevo ID de sesión. De ésta forma, por más que hayamos usado un ID insertado por el atacante antes de loguearnos, una vez logueados utilizaremos un ID totalmente distinto y desconocido por el atacante. Otra opción es regenerar los identificadores de sesión para sesiones nuevas. Esto se consigue poniendo una "marca" en las sesiones de la página web. Cada vez que se hace un session_start(), si la sesión no tiene la "marca", cambiamos su identificador por uno nuevo (session_regenerate_id(true);) y desconocido para el atacante. Ejemplo:
<?php
session_start();

if (isset($_SESSION['mark']) === false)
{
 session_regenerate_id(true);
 $_SESSION['mark'] = true;
}
?>
Cambiar el identificador de sesión siempre que un usuario cambie su estado (autenticado, registrado, ...). Con la anterior medida de protección puede parecer que ya estamos protegidos, pero un atacante puede entrar en la página web, generar una sesión con "marca" y después mandar el identificador de esa sesión al usuario atacado. Así el usuario atacado acabaría con una sesión con "marca" y conocida por el atacante. Por eso, se debe regenerar el identificar de sesión siempre que un usuario realice una acción que le obligue a autenticarse. Ejemplo:
<?php
if ($user_logged_in === true)
{
 session_regenerate_id(true);
 $_SESSION['logged'] = true;
}
?>
Aviso: es muy importante que sea session_regenerate_id(true); y no session_regenerate_id(); ya que si no eliminamos la sesión antigua, estamos haciendo más fácil los ataques contra la página web al dejar múltiples sesiones válidas abiertas en el servidor. Sistema de logout: dar a los usuarios una forma de salir de su cuenta y destruir la sesión. Dentro de tu página coloca visiblemente un espacio con la opción de salir para un usuario registrado y coloca este código
<?php
// destruir informacion de session en el server
session_unset();
session_destroy(); 

// destruir información de session en el cliente 
$session_cookie_params = session_get_cookie_params(); 
setcookie(session_name(), '', time() - 24 * 3600, $session_cookie_params['path'], $session_cookie_params['domain'], $session_cookie_params['secure'], $session_cookie_params['httponly']); 

// Limpiar el array $_SESSION
$_SESSION = array();  
?>
Verificación doble. Usar un segundo método para intentar reconocer al usuario de la sesión. Esto puede hacerse guardando cabeceras como HTTP_USER_AGENT (navegador del usuario) o REMOTE_ADDR (IP del usuario) cuando se crea la sesión, de esta forma: Para ello podemos guardar una serie de variables de sesión de los datos que el usuario nos proporciona en cada visita, como son la dirección IP o la información de su navegador, etc. Si estos cambian significaría que es un intento de session hijacking y por tanto es un usuario que realiza un ataque. En el siguiente ejemplo tenemos dos archivos, en el que en el primer archivo realizamos un login satisfactorio y guardamos los datos iniciales de dirección IP y tipo del navegador con el que lo hemos hecho:
<?php
session_start();
$_SESSION['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['HTTP_USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
?>
En pantallas posteriores, comprobaremos si en cada pantalla los datos actuales de dirección IP y datos del navegador se corresponden con los que se realizó el login como usuario en la web:
<?php
session_start();
if($_SESSION['REMOTE_ADDR'] != $_SERVER['REMOTE_ADDR'] || $_SESSION['HTTP_USER_AGENT'] != $_SERVER['HTTP_USER_AGENT']) {
    exit();
}
?>
Una aplicación de las medidas expuestas anteriormente es:
<?php
// obtener siempre esta info en cada pagina
$aip = $_SERVER['REMOTE_ADDR'];
$bip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$agent = $_SERVER['HTTP_USER_AGENT'];
session_start();

// crear cuando el usuario se loguee
$_SESSION['ident'] = hash("sha256", $aip . $bip . $agent);

// verificar en cada pagina que requiera usuario registrado
if(!isset($_SESSION['ident'])){

// hacer esto en cada pagina que visite un usuario registrado
$ident = hash("sha256", $aip . $bip . $agent);
if ($ident != $_SESSION['ident'])
{
    end_session();
    header("Location: login.php");
}
}
Exit(“Intento de Hijacking”);
?>
Un ejemplo más sencillo podría ser
<?php
session_start();

if (isset($_SESSION['HTTP_USER_AGENT']))
{
    if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT']))
    {
        /* Prompt for password */
        exit;
    }
}
else
{
    $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
}
?>
O bien crear una function
<?php
static protected function preventHijacking()
{
 if(!isset($_SESSION['IPaddress']) || !isset($_SESSION['userAgent']))
  return false;

 if ($_SESSION['IPaddress'] != md5($_SERVER['REMOTE_ADDR']))
  return false;

 if( $_SESSION['userAgent'] != md5($_SERVER['HTTP_USER_AGENT']))
  return false;

 return true;
}
?>
Otra forma de prevenir hijacking es verificando el $_SERVER['HTTP_REFERER'] pero antes de utilizarlo dale una ojeada a esto: The address of the page (if any) which referred the user agent to the current page. This is set by the user agent. Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature. In short, it cannot really be trusted. http://php.net/manual/en/reserved.variables.server.php Igual aqui dejo el codigo
<?php
if(isset($_SERVER['HTTP_REFERER']) && (strpos($_SERVER['HTTP_REFERER'], "http://tu_direccion") === 0)) {
  echo "sigues con tus validaciones, no confies plenamente";   
   }
else
{
   echo "Busca otra forma de validar";
}
?>

Caducar sessiones PHP

En el php.ini esta un atributo llamado 'session.cookie_lifetime', si está en 0 va a durar hasta que el navegador se cierre tambien puedes indicar cuanto debe durar la cookie de sesion, en esta línea session.gc_maxlifetime = 1440 que esta dado en segundos, cambias el 1440 por el número de segundos que estimes conveniente. Si no tienes acceso al php.ini puedes utilizar session_set_cookie_params con su parametro respectivo que es lifetime en este caso se termina sesión cuando el usuario cierra el explorador web:
<?php
session_set_cookie_params(0);
session_start();
?>
Para que funcione debes eliminar debidamente las cookies de session cuando un usuario termine session. Este script maneja valida el tiempo establecido de vida para una sesión, en caso de que la validación haya expirado destruye la sesión y redirige al usuario al área de login para que vuelva a registrarse , el siguiente código debe colocarse en la página de inicio de sesión
<?php
session_start(); 
$_SESSION['tiempo']=time();  
?>
Este script deberá colocarse en toda página que requiera una sesión de usuario autorizada
<?php
session_start(); 
include('archivo.php'); 
..... 
..... 
?>
Y este será el contenido de archivo.php que se encargara de cerrar una sesión si es que esta ha expirado de acuerdo a un tiempo establecido por nosotros a conveniencia
<?php
$time = 3600; // una hora en mili-segundos 

// verificamos si existe la sesión 
// el nombre "session_name" es como ejemplo 
if(isset($_SESSION["session_name"])) 
{ 
      // verificamos si existe la sesión que se encarga del tiempo 
      // si existe, y el tiempo es mayor que una hora, expiramos la sesión  
      if(isset($_SESSION["expire"]) && time() > $_SESSION["expire"] + $time) 
      { 
           echo'';  
       // también puedes utilizar header(“Location:index.php”);
         
     // destruir informacion de session en el server
 
          unset($_SESSION["expire"]); 
          unset($_SESSION["session_name"]); 
         session_unset();
         session_destroy(); 

// destruir informacion de session en el cliente 
$session_cookie_params = session_get_cookie_params(); 
setcookie(session_name(), '', time() - 24 * 3600, $session_cookie_params['path'], $session_cookie_params['domain'], $session_cookie_params['secure'], $session_cookie_params['httponly']); 

// Limpiar el array $_SESSION
$_SESSION = array();  

      } 
      // creamos o redefinimos el valor de la variable de session 
      else 
      { 
           $_SESSION["expire"] = time(); 
       } 
} 
?>

29 de abril de 2015

Session Fixation PHP

Session fixation Session fixation (fijación de sesión) es un método de Session hijacking (robo de sesión) un poco especial, ya que, si normalmente en el robo de sesión se intenta conseguir el identificador de sesión de un usuario ya autenticado, la fijación de sesión se basa en asignar un identificador de sesión conocido a un usuario antes de que se autentique. Lo malo de este ataque es que es muy fácil de realizar. Lo bueno, es que es muy fácil de prevenir. Supongamos que la página web atacada utiliza el nombre de sesión por defecto en PHP: PHPSESSID. El atacante sólo tiene que crear un enlace a la página web objetivo de esta forma: http://www.web-atacada.com/?PHPSESSID=123456 Después, sólo tiene que hacer que el usuario al que quiere atacar utilice ese enlace para acceder a la página web. Esto se puede conseguir de varias formas: enviándole el enlace por email, a través de una red social o, incluso, sin que el usuario se dé cuenta, haciendo una redirección desde una página web del atacante. Una vez que el usuario utiliza este enlace, le está diciendo a la página web que su identificador de sesión es 123456. PHP al comprobar que no existe una sesión con ese identificador, la crea y se la asigna al usuario. Si este se autentica en el sitio, el atacante, que conoce su identificador de sesión (123456), puede hacerse con su cuenta haciéndose pasar por él. Atención: aunque la página web utilice cookies para la propagación del identificador de sesión, normalmente la configuración de PHP acepta el identificador en la URL (después lo propaga a través de cookies pero cuando ya ha creado la sesión con ese identificador). También hay que tener en cuenta que un atacante puede (con técnicas mucho más avanzadas y aprovechando errores en las directivas de seguridad de los navegadores) realizar este ataque insertando una cookie al usuario. Aunque es la forma más sencilla, este ataque no se limita sólo a la propagación por URL. MÉTODOS DE PREVENCIÓN Si bien es un ataque que se puede realizar fácilmente, la prevención es igualmente fácil. Podemos tomar las siguientes medidas: Ten cuidado donde guardas la informacion de la sesión del usuario, por omisión, PHP usa el gestor interno de almacenamiento files, el cual se establece mediante session.save_handler. Éste guarda los datos de sesión en el servidor en la ubicación especificada por la directiva de configuración session.save_path, pode defecto se encuentra en session.save_path = "c:/wamp/tmp" Utilizar únicamente cookies para la propagación del identificador de sesión. Esta medida no anula el peligro, pero hace más difícil el ataque (y muchos otros). En PHP se consigue así:
<?php
ini_set( 'session.use_only_cookies', TRUE );    
ini_set( 'session.use_trans_sid', FALSE );
?>
Tener en cuenta que el codigo anterior se debe incluir al principio de cada script php pero tambien pueden definirise en php.ini, lo que se busca es que la informacion sid del usuario no pueda obtenerse mediante $_GET['PHPSESSID'] sino solamente a traves de cookies, tambien se especifica a off el session.use_trans_sid para evitar leaking session ID en las URL’s Aparte se debe generar nuevos IDs de sesión para distintos clientes. El programa del lado del servidor nunca debería usar IDs entregados por el browser para un usuario logueado, esto es, una vez que el usuario se loguea en el sistema, éste debería generar automáticamente un nuevo ID de sesión y utilizar éste en el futuro. Desde el código que realiza el login debería utilizarse sesion_regenerate_id() a fin de no utilizar el que por defecto entrega PHP
<?php
if(($_POST['user'] == "demasiadovivo") && ($_POST['pass'] == "123456"))  
{  
    session_regenerate_id(true);  
    $_SESSION['user'] = "demasiadovivo";      
}  
?>
La función session_regenerate_id() crea un nuevo ID de sesión. De ésta forma, por más que hayamos usado un ID insertado por el atacante antes de loguearnos, una vez logueados utilizaremos un ID totalmente distinto y desconocido por el atacante. Otra opción es regenerar los identificadores de sesión para sesiones nuevas. Esto se consigue poniendo una "marca" en las sesiones de la página web. Cada vez que se hace un session_start(), si la sesión no tiene la "marca", cambiamos su identificador por uno nuevo (session_regenerate_id(true);) y desconocido para el atacante. Ejemplo:
<?php
session_start();

if (isset($_SESSION['mark']) === false)
{
 session_regenerate_id(true);
 $_SESSION['mark'] = true;
}
?>
Cambiar el identificador de sesión siempre que un usuario cambie su estado (autenticado, registrado, ...). Con la anterior medida de protección puede parecer que ya estamos protegidos, pero un atacante puede entrar en la página web, generar una sesión con "marca" y después mandar el identificador de esa sesión al usuario atacado. Así el usuario atacado acabaría con una sesión con "marca" y conocida por el atacante. Por eso, se debe regenerar el identificar de sesión siempre que un usuario realice una acción que le obligue a autenticarse. Ejemplo:
<?php
if ($user_logged_in === true)
{
 session_regenerate_id(true);
 $_SESSION['logged'] = true;
}
?>
Aviso: es muy importante que sea session_regenerate_id(true); y no session_regenerate_id(); ya que si no eliminamos la sesión antigua, estamos haciendo más fácil los ataques contra la página web al dejar múltiples sesiones válidas abiertas en el servidor.

Definir un tiempo de vida a la variable de sesión de PHP. Después de dar algunas pautas contra los ataques más usuales de robo de las sesiones, otra buena práctica es definir un tiempo de vida a las sesiones para que un usuario logueado no se quede “dormido” con la sesión abierta. En nuestra aplicación tendrá un limite de tiempo y cuando se agote se redireccionará a una página web para su nuevo acceso a la plataforma y creación de una nueva sesión. xxxxxxxx

Definir un tiempo de inactividad de session En el php.ini esta un atributo llamado 'session.cookie_lifetime', si está en 0 va a durar hasta que el navegador se cierre tambien puedes indicar cuanto debe durar la cookie de sesion, en esta línea session.gc_maxlifetime = 1440 que esta dado en segundos, cambias el 1440 por el número de segundos que estimes conveniente. Si no tienes acceso al php.ini puedes utilizar session_set_cookie_params con su parámetro respectivo que es lifetime en este caso se termina sesión cuando el usuario cierra el explorador web:
<?php
session_set_cookie_params(0);
session_start();
?>
Para que funcione debes eliminar debidamente las cookies de session cuando un usuario termine session. Este script maneja valida el tiempo establecido de vida para una sesión, en caso de que la validación haya expirado destruye la sesión y redirige al usuario al área de login para que vuelva a registrarse , el siguiente código debe colocarse en la página de inicio de sesión
<?php
session_start(); 
$_SESSION['tiempo']=time();  
?>
Este script deberá colocarse en toda página que requiera una sesión de usuario autorizada
<?php
session_start(); 
include('archivo.php'); 
..... 
..... 
?>
Y este será el contenido de archivo.php que se encargara de cerrar una sesión si es que esta ha expirado de acuerdo a un tiempo establecido por nosotros a conveniencia
<?php
$time = 3600; // una hora en mili-segundos 

// verificamos si existe la sesión 
// el nombre "session_name" es como ejemplo 
if(isset($_SESSION["session_name"])) 
{ 
      // verificamos si existe la sesión que se encarga del tiempo 
      // si existe, y el tiempo es mayor que una hora, expiramos la sesión  
      if(isset($_SESSION["expire"]) && time() > $_SESSION["expire"] + $time) 
      { 
           echo'';  
       // también puedes utilizar header(“Location:index.php”);
         
     // destruir informacion de session en el server
 
          unset($_SESSION["expire"]); 
          unset($_SESSION["session_name"]); 
         session_unset();
         session_destroy(); 

// destruir informacion de session en el cliente 
$session_cookie_params = session_get_cookie_params(); 
setcookie(session_name(), '', time() - 24 * 3600, $session_cookie_params['path'], $session_cookie_params['domain'], $session_cookie_params['secure'], $session_cookie_params['httponly']); 

// Limpiar el array $_SESSION
$_SESSION = array();  

      } 
      // creamos o redefinimos el valor de la variable de session 
      else 
      { 
           $_SESSION["expire"] = time(); 
       } 
} 
?>

Si vas a modificar el php.ini te vale esta información

session.gc_maxlifetime Este valor (por defecto 1440 segundos) define cuánto tiempo se mantendrá viva una sesión PHP sin utilizar. Por ejemplo: un usuario inicia sesión, busca a través de su aplicación o sitio web , por hora, por día. No hay problema. Mientras el tiempo entre sus clics nunca exceda 1.440 segundos. Es un valor de tiempo de espera. Colector de basura la sesión de PHP se ejecuta con una probabilidad definida por session.gc_probability dividido por session.gc_divisor . Por defecto es 1/100, lo que significa que el valor de tiempo de espera anterior se comprueba con una probabilidad de 1 en 100. Si dejas un valor de 100% es decir: gc_probability/gc_divisor = 1/10000 va a hacer que el servidor sobre-cargue a PHP indicando que en todas las peticiones haga una limpieza de las sesiones. Lo mejor en caso de ser un servidor que tiene poca visita colocarlo a que sea un 1% es decir 1/100 (también puede ser un 10% si tu servidor lo estás usando para desarrollar, es decir 1/1000). Si tiene mucha visita lo mejor es colocar a que sea 0.1% es decir 1000/1.

session.cookie_lifetime Este valor (por defecto 0, es decir, hasta el próximo reinicio del navegador) define el tiempo (en segundos) vivirá una cookie de sesión. Suena similar a session.gc_maxlifetime , pero es un enfoque completamente diferente. Este valor define indirectamente el "absoluto" tiempo de vida máximo de una sesión, si el usuario está activo o no. Si este valor se establece en 60, cada sesión termina después de una hora. Al crear mis antiguas aplicaciones en PHP, solía controlar la duración de las sesiones con únicamente un parámetro, session.gc_maxlifetime, que por defecto está ajustado a 1440 segundos (24 minutos), ya que lo cambiaba a 7200 segundos (dos horas, o 120 minutos). Aunque no es una técnica recomendada, solía modificar el parámetro en php.ini. El primer problema era que obligaba a todas las aplicaciones del servidor a permitir que las sesiones duraran 2 horas. El segundo era que una actualización en el servidor producía que la caducidad volviera a 1440 segundos. Es decir, la duración o caducidad de las sesiones no puede dejarse en manos de php.ini, sino de la propia aplicación. Podemos correr el riesgo de modificar ambos parámetros desde php.ini: session.cookie_lifetime 7200 session.gc_maxlifetime 7200 O mejor aún, colocarlos en cada página que utilice sesiones:
<?php
ini_set("session.cookie_lifetime","7200");
ini_set("session.gc_maxlifetime","7200");
session_start();
$_SESSION["ejemplo"]="invitado";
?>
Recuerda ponerlo antes de la instrucción “session_start()” y además verificar la probabilidad de gc sea 1/100 Terminar sesión apropiadamente

<?php
// destruir información de session en el server
session_unset();
session_destroy(); 

// destruir informacion de session en el cliente 
$session_cookie_params = session_get_cookie_params(); 
setcookie(session_name(), '', time() - 24 * 3600, $session_cookie_params['path'], $session_cookie_params['domain'], $session_cookie_params['secure'], $session_cookie_params['httponly']); 

// Limpiar el array $_SESSION
$_SESSION = array();  
?>

Login PHP con crypt() y Blowfish

En esta entrada se utilizara la función crypt() y Blowfish, otra opción muy aceptable es utilizar password_hash(), dejo este link con una explicación del tema en Login PHP con password_hash(), pasando a la función crypt() con Blowfish como algoritmo de hash lo que se busca es mejorar un método común para proteger passwords como este:
<?php
function hash_password($password, $salt) 
{
    $hash = hash_hmac('SHA512', $password, $salt);
    for ($i = 0; $i < 5000; $i++) 
    {
        $hash = hash_hmac('SHA512', $hash, $salt);
    }
    
    return $hash;
}

$nombre = $_POST['nombre'];
$password = $_POST['password'];

$salt = str_replace('=', '.', base64_encode(mcrypt_create_iv(20)));
$hash = hash_password($password, $salt);
var_dump($hash);
?>
Por una codificación mas fuerte, la función hash que utilizaremos será crypt() que soporta de forma nativa varios algoritmos. Por defecto usa la codificación MD5 pero por razones de seguridad usaremos Blowfish, también puede utilizarse Verificando si mi versión de PHP soporta Blowfish El algoritmo Blowfish está disponible a partir de la versión 5.3 de PHP. Si quieres comprobar que tienes soporte para Blowfish puedes colocar este código en un archivo PHP:
<?php
  if(defined("CRYPT_BLOWFISH") && CRYPT_BLOWFISH) {
    echo "CRYPT_BLOWFISH esta disponible";
  }
?>
Si podemos usar Blowfish pasaremos a codificar la contraseña, en el caso de que no, tendremos que instalar la última versión de PHP. Para forzar que crypt() utilice el algoritmo Blowfish debemos colocar un salt especial, si la función no reconoce el salt que escribimos, el hash resultante estará codificado en MD5. La función queda asi:
<?php
crypt($password, '$2a$10$'.$unique_salt);
?>
El segundo parámetro es un poco raro, pero en realidad lo que hay ahí son instrucciones que deben seguir la salt para Blowfish $2y$ Indica que vamos a usar BlowFish. $10$ Indica el costo del cálculo. Puede ir de 04 a 31., con 10 como valor aceptable. $unique_salt Indica un salt para el cálculo del algoritmo. Una palabra/frase alfa-numérica de 22 caractéres (por lo menos así es para Blowfish) que pueden ser ./1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz. Teniendo en cuenta todo lo anterior, un script para registrar un usuario sería más o menos así:
<?php
$nombre = $_POST['nombre'];
$password = $_POST['pass'];

// Generamos un salt aleatoreo, de 22 caracteres para Bcrypt
$salt = substr(base64_encode(openssl_random_pseudo_bytes('30')), 0, 22);

// A Crypt no le gustan los '+' así que los vamos a reemplazar por puntos.
$salt = strtr($salt, array('+' => '.')); 

// Generamos el hash
$hash = crypt($password, '$2y$10$' . $salt);

// Guardamos los datos en la base de datos
$db = new PDO(.....);
$stmt = $db->prepare('INSERT INTO usuarios (nombre, pass) VALUES (?, ?)');
$stmt->execute(array($nombre, $hash)); 
?>
Y para validar la contraseña que esta almacenada en la base de datos contra un valor proporcionado por el usuario sería algo así:
<?php
$nombre = $_POST['nombre'];
$password = $_POST['pass'];

// Validamos $nombre, bla bla bla..

// Extraemos el hash de la base de datos
$db = new PDO(......);
$stmt = $db->prepare('SELECT pass
                      FROM usuarios
                      WHERE nombre = ?');
                      
$stmt->execute(array($nombre));
$dbHash = $stmt->fetchcolumn();

// Recalculamos a ver si el hash coincide.
if (crypt($password, $dbHash) == $dbHash)
    echo 'El usuario ha sido autenticado correctamente';
else
    die('Mal Password');
?>
Creando una función que haga el trabajo Para que nuestra contraseña sea más segura, vamos a crear una función que realice un salt aleatorio. El valor que se generará aleatoriamente para cada hash serán los últimos 22 caracteres.
<?php
function crypt_blowfish($password, $digito = 7) {
$set_salt = './1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$salt = sprintf('$2a$%02d$', $digito);
for($i = 0; $i < 22; $i++)
{
 $salt .= $set_salt[mt_rand(0, 22)];
}
return crypt($password, $salt);
}
?>
Para crear el hash con la función que recién creamos solamente tenemos que escribirlo de la siguiente manera
<?php
$password = crypt_blowfish('stringdondeestaralacontraseña');
?>
Ahora para validar la contraseña que recién se ha creado y suponiendo que se guardo en la base de datos, vamos a recuperar ese valor en la variable $passwordenBD lo que se obtiene es:
<?php
$passwordenBD = '$2a$07$yMoJrJpwEPrmVnZx4KIyNuOAiOMQksjkV1EW0YRgVe33eYe/yT60y';

//Micontraseña representa el valor capturado desde el usuario

if( crypt('micontraseña', $passwordenBD) == $passwordenBD) {
 echo 'OK';
}
?>

Login PHP con password_hash()

Primero una aclaración…como no guardar contraseñas No guardar contraseñas en texto plano, esto debería ser obvio ya que si alguien tiene acceso a tu base de datos y a las contraseñas de tus usuarios, si un ataque externo…o interno(en quien confias?)…expone las contraseñas de tus usuarios, tu trabajo como programador-analista-diseñador-DBA-conserje –prepara café se verá comprometido, ni hablar de las represalias directas de la compañía que se lavaran las manos en tu incapacidad. Si a esto le sumamos que muchos usuarios, como yo, utilizan la misma contraseña para muchos sitios pues el pecado habrá sido mayor, recuerda que no solo te expones a ataques desde fuera de tu empresa ya que hasta un shared server puede fácilmente entrar a tu base de datos. No inventes, a lo mejor no eres un expert en seguridad, lo mejor es probar con soluciones que han sido aceptadas por la comunidad de programadores que como tu están buscando proteger las valiosas contraseñas de sus usuarios. No encriptar contraseñas Había un profesor de la universidad que quería llamar la atención haciéndonos ver que la pablara encriptar no existe más nunca explico el problema de encriptar contraseñas…bueno, el gran problema es que el encriptar una frase es un proceso reversible, esto es, que al encriptar pabletoreto podría obtener algo como esto: hjb32 y pareciera que estaría seguro pero qué tal si desencripto ese texto y obtengo pablo de nuevo? Utilizar Hash pero no MD5 Si no sabes que es un hash te lo contamos. No es más que una secuencia de caracteres única que representa una entidad ( en nuestro caso una contraseña), esta secuencia se obtiene mediante algoritmos hash (generalmente md5 o sha1), las características de estos algoritmos que nos resulta útil para gestionar las contraseñas son: Algoritmos de una sola vía: No hay forma de obtener la cadena original partiendo de la secuencia obtenida. Son deterministas: Es decir, la misma entrada siempre producirá la misma secuencia de salida. Son “económicas” de calcular: Esto quiere decir que no se necesitan grandes cantidades de tiempo y memoria para calcularlas, haciendo factible su implementación en nuestras aplicaciones. Y es precisamente esta última cualidad que actualmente ha comprometido la efectividad de estos algoritmos hash, debido al incremento de la capacidad computacional de los equipos con los que trabajamos y el empleo conjunto con técnicas de “crack” de hash como las tablas arcoiris ( Rainbow tables), las que nos obligan a ir un paso más allá e implementar técnicas que le dificulten o por lo menos no hagan atractivo computacionalmente hablando el tratar de romper nuestras contraseñas. Utilizar hash para las contraseñas es ir en el camino correcto pero no te vayas por las opciones MD5 o SHA-1 porque son propensas a ataques como fuerza bruta o rainbow tables, para más información acerca de estos ataques pregúntale a Yahoo. LinkedIn o a la ultra segura portentosa y divina Apple. Como sugerencia utiliza Bcrypt Utiliza SALT Para aumentarle la dificultad a los crackers, hay 2 métodos muy comunes que son salting y peppering. En el caso de un salt, La idea es crear un string aleatoreo y añadirlo al password. Ese string nunca lo debe saber el usuario, de hecho el nisiquiera debería saber que existe. El objetivo de este método es aumentar la longitud y complejidad del password original. Por otro lado un pepper sería otro string aleatoreo, que se guarda generalmente por fuera de la base de datos y se le añade a un salt. Su función principal es generar distintos hashes para distintos dominios/páginas. Personalmente pienso que un pepper no añade beneficios gigantes a la seguridad de una aplicación, asi que no hablaré mucho al respecto. Las Tablas Arcoiris y las Lookup Tables, solo funcionan cuando cada password ha sido hasheado de la misma forma. Si 2 usuarios tienen el mismo password, su hash sería el mismo. Esa situación se previene, creando un salt para cada password, cosa que si 2 usuarios usan el mismo password, el resultado serían 2 hashes distintos! De preferencia no utilices una salt estatica sino una que se genere aleatoriamente, la clave en todo este asunto es nunca reusar un mismo salt, siempre generar uno nuevo y preferiblemente, que tenga una longitúd decente (pongale unos 20 o 32 caracteres). Para generar salts aleatoreos en PHP, lo mejor es usar las funciónes mcrypt_create_iv o openssl_random_pseudo_bytes - nada de mt_rand, rand, uniqid, ni algo inventado por nosotros mismos, entonces en vez de tener esto
<?php
$salt = '34a@$#aA9823$';
$password = 'dinvaders1234';
$password = hash('sha256', $salt . $password);
?>
opta por cualquiera de estas dos opciones para generar datos aleatorios, primero considera usar mcrypt_create_iv que crea un vector de inicialización (IV) desde una fuente aleatoria, el primer parametro representa el tamaño del vector y el segundo parametro representa la fuente del IV. El parámetro fuente puede ser MCRYPT_RAND (generador de números aleatorios del sistema), MCRYPT_DEV_RANDOM (lee datos de /dev/random) y MCRYPT_DEV_URANDOM (lee datos de /dev/urandom). Antes de 5.3.0, MCRYPT_RAND era la única soportada en Windows. Obsérvese que el valor predeterminado de este parámetro era MCRYPT_DEV_RANDOM antes de PHP 5.6.0.
<?php
// A Crypt no le gustan los '+' así que los vamos a reemplazar por puntos.
$salt = strtr(base64_encode(mcrypt_create_iv(22, MCRYPT_DEV_URANDOM)), '+', '.');
?>
o bien esta segunda opcion que utiliza openssl_random_pseudo_bytes que genera una cadena de bytes pseudo-aleatoria, con el número para crear aleatoriamente 22 numeros:
<?php
// Generamos un salt aleatorio, de 22 caracteres para Bcrypt
$salt = substr(base64_encode(openssl_random_pseudo_bytes('30')), 0, 22);

// A Crypt no le gustan los '+' así que los vamos a reemplazar por puntos.
$salt = strtr($salt, array('+' => '.')); 
?>
crypt — Hash de cadenas de un sólo sentido(PHP 4, PHP 5) Vamos a lo importante, primero mostrare como utilizar la función crypt sobre una contraseña y como validarla, no ingresare la contraseña a la base de datos
<?php
$username = 'Admin';
$password = 'pabletoreto';
// costo base 2
$cost = 10;
// Creamos una salt aleatoria de 16 elementos
$salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');
// $2a$ significa Blowfish algorithm y %02d$ es el costo 
// deben ir antes de la salt para ser un formato aceptado
// por la funcion crypt
$salt = sprintf("$2a$%02d$", $cost) . $salt;

$hash = crypt($password, $salt);
echo "
"; echo "
"; echo "se obtuvo este valor encryptado" .$hash; echo "
"; echo "
"; echo "Ahora probaremos la validacion con pabletoreto de contraseña"; echo "
"; $password2="pabletoreto"; //Vamos a verificar if(password_verify($password2, $hash)){ echo "OK, validacion correcta"; } else { echo "validacion incorrecta"; } ?>
Podríamos haber verificado la contraseña con if ( hash_equals($hash, crypt($password2, $hash))) pero debes verificar que tu versión de PHP lo permita. crypt() devolverá el hash de un string utilizando el algoritmo estándar basado en DES de Unix o algoritmos alternativos que puedan estar disponibles en el sistema. El parámetro salt es opcional. Sin embargo, crypt() crea una contraseña débil sin salt. PHP 5.6 o posterior emiten un error de nivel E_NOTICE sin él. Asegúrese de especificar una sal lo suficientemente fuerte para mayor seguridad. password_hash — Crea un nuevo hash de contraseña (PHP 5 >= 5.5.0) password_hash() utiliza un hash fuerte, genera una sal fuerte, y aplica los redondeos necesarios automáticamente. password_hash() es una envoltura simple de crypt() compatible con los hash de contraseñas existentes. Se aconseja el uso de password_hash(). Lo bueno de password_hash() es que la misma función nos crea una salt segura, esta será aleatoria y diferente para cada password, el coste por defecto es 10 el cual es suficiente, estos dos valores ya están presentes en el hash que password_hash entrega, además que si utilizamos PASSWORD_DEFAULT como segundo parámetro es como que estuviéramos utilizando el algoritmo de hash BCRYPT, pero si queremos utilizar el algoritmo recomendado CRYPT_BLOWFISH se debe especificar como segundo parametro PASSWORD_BCRYPT. password_hash() crea un nuevo hash de contraseña usando un algoritmo de hash fuerte de único sentido. password_hash() es compatible con crypt(). Por lo tanto, los hash de contraseñas creados con crypt() se pueden usar con password_hash(), significa que podemos utilizar password_verify() para ambos casos. Actualmente, se tiene soporte para los siguientes algoritmos: PASSWORD_DEFAULT - Usar el algoritmo bcrypt (predeterminado a partir de PHP 5.5.0). Observe que esta constante está diseñada para cambiar siempre que se añada un algoritmo nuevo y más fuerte a PHP. Por esta razión, la longitud del resultado de usar este identificador puede cambiar en el tiempo. Por lo tanto, se recomienda almacenar el resultado en una columna de una base de datos que pueda apliarse a más de 60 caracteres (255 caracteres sería una buena elección). PASSWORD_BCRYPT - Usar el algoritmo CRYPT_BLOWFISH para crear el hash. Producirá un hash estándar compatible con crypt() utilizando el identificador "$2y$". El resultado siempre será un string de 60 caracteres, o FALSE en caso de error. Opciones admitias: salt - para proporcionar manualmente una sal a usar cuando se realiza el hash de la contraseña. Observe que esto sobrescribirá y prevendrá que una sal sea generada automáticamente. Si se omite, se generará una sal aleatoria mediante password_hash() para cada contraseña con hash. Este es el modo de operación previsto. cost - que denota el coste del algoritmo que debería usarse. Se pueden encontrar ejemplo de estos valores en la página de crypt(). Si se omite, se usará el valor predeterminado 10. Este es un buen coste de referencia, pero se podría considerar aumentarlo dependiendo del hardware. Entonces utilicemos password_hash, en la pagina de PHP se muestra este ejemplo de password_hash()
<?php
/**
 * Queremos realizar un hash a nuestra contraseña cuando el algoritmo DEFAULT actual.
 * Actualmente es BCRYPT, y producirá un resultado de 60 caracteres.
 *
 * A que tener en cuenta que DEFAULT puede cambiar con el tiempo, por lo que debería prepararse 
 * para permitir que el almacenamento se amplíe a más de 60 caracteres (255 estaría bien)
 */
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT)."\n";
?>
Ejemplo de password_hash() estableciendo el coste manualmente
<?php
/**
 * En este caso, queremos aumentar el coste predeterminado de BCRYPT a 12.
 * Observe que también cambiamos a BCRYPT, que tendrá siempre 60 caracteres.
 */
$opciones = [
    'cost' => 12,
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $opciones)."\n";
?>
Ejemplo más completo con mysqli, aquí la conexión a la base de datos se encuentra en conexión.php pero esa la haces tu solo la utilizo emplicitamente en este código, además se dan las opciones de ingresar un nuevo password y de verificar password existentes, fíjate en elos $_POST[‘registrar’] y $_POST[‘ingresar’] y para verificar la contraseña contra el hash generado se utiliza password_verify($password_a_verificar, $hash_generado))
<?php
session_start();
ob_start();
require_once "conexion.php";
error_reporting(E_ALL);

if((isset($_POST['ingresar'])) or (isset($_POST['registrar']))){
  
    if(!($mysqli = conectarse())){
 echo "Error al conectarse a la base de datos";
 echo "
"; echo $mysqli->connect_errno. " - " .$mysqli->connect_error; exit(); } $errores = array(); $username = filter_var($username, FILTER_SANITIZE_STRING); $password = filter_var($password, FILTER_SANITIZE_STRING); if( empty($username) ) $errores[] = "Debe especificar username"; if( empty($password) ) $errores[] = "Debe especificar password"; if( count($errores) > 0 ) { echo "

ERRORES ENCONTRADOS:

"; for( $contador=0; $contador < count($errores); $contador++ ) echo $errores[$contador]."
"; exit(); } if(isset($_POST['ingresar'])) { if(!($stmt = $mysqli->prepare("SELECT username, password FROM login_php WHERE username = ?"))){ echo "Prepare failed: (" . $mysqli->errno . ")" . $mysqli->error; exit(); } if(!$stmt->bind_param('s', $username)){ echo "Bind failed: (" . $stmt->errno . ")" . $stmt->error; exit(); } if(!$stmt->execute()){ echo "Execute failed: (" . $stmt->errno .")" . $stmt->error; exit(); } $userdata = $stmt->get_result(); $row = $userdata->fetch_array(MYSQLI_ASSOC); $stmt->store_result(); if(password_verify($password, $row['password'])){ $_SESSION['user'] = $username; header('Location: formulario.html'); exit(); }else{ echo "Login Failed: (" . $stmt->errno .")" . $stmt->error; exit(); } } if (isset($_POST['registrar'])){ $password_hash = password_hash($password, PASSWORD_DEFAULT); if(!($stmt = $mysqli->prepare("INSERT INTO login_php (username, password) VALUES (?,?)"))){ echo "Prepare failed: (" . $mysqli->errno . ")" . $mysqli->error; } if(!$stmt->bind_param('ss', $username, $password_hash)){ echo "Binding paramaters failed:(" . $stmt->errno . ")" . $stmt->error; } if(!$stmt->execute()){ echo "Execute failed: (" . $stmt->errno .")" . $stmt->error; } if($stmt) { header('Location: form_login.html'); } else{ echo "Registration failed"; } } }else{ header('location:form_login.html'); } $contenido = ob_get_contents(); ob_end_clean(); $stmt->close(); $mysqli->close(); ?>
El algoritmo, coste y sal usados son devueltos como parte del hash. Por lo tanto, toda la información que es necesaria para verificar el hash, está incluida en él. Esto permite que la función password_verify() verifique el hash sin tener que almacenar por separado la información de la sal o del algoritmo. Uso alternativo
<?php
/**
 * Observe que la sal se genera aleatoriamente aquí.
 * No use nunca una sal estática o una que no se genere aleatoriamente.
 *
 * Para la GRAN mayoría de los casos de uso, dejar que password_hash genere la sal aleatoriamente
 */
$opciones = [
    'cost' => 11,
    'salt' => mcrypt_create_iv(22, MCRYPT_DEV_URANDOM),
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $opciones)."\n";
?>
Ejemplo de password_hash() buscando un buen coste
<?php
/**
 * Este código evaluará el servidor para determinar el coste permitido.
 * Se establecerá el mayor coste posible sin disminuir demasiando la velocidad
 * del servidor. 8-10 es una buena referencia, y más es bueno si los servidores
 * son suficientemente rápidos. El código que sigue tiene como objetivo un tramo de
 * ≤ 50 milisegundos, que es una buena referencia para sistemas con registros interactivos.
 */
$timeTarget = 0.05; // 50 milisegundos 

$coste = 8;
do {
    $coste++;
    $inicio = microtime(true);
    password_hash("test", PASSWORD_BCRYPT, ["cost" => $coste]);
    $fin = microtime(true);
} while (($fin - $inicio) < $timeTarget);

echo "Coste conveniente encontrado: " . $coste . "\n";
?>