jueves, 17 de abril de 2014

[Parte 11] Seguridad en PHP

Criptografia

Literalmente <<escritura oculta>> tradicionalmente se ha definido como parte de la criptologia que se ocupa de las tecnicas, bien sea aplicadas al arte o la ciencia, que alteran las representaciones lingüisticas de mensajes, mediante tecnicas de cifrado o codificado, para hacerlos ininteligibles a intrusos (lectores no autorizados) que interceptan esos mensajes. Por tanto el unico objetivo de la criptografia era conseguir la confidencialidad de los mensajes. Para ello se diseñaban sistemas de cifrado y codigos. 

Los tipos fundamentales de criptografia que un desarrollador en PHP debe conocer son los siguientes:

- La criptografia simetrica
- La criptografia asimetrica (clave publica)
- Funciones hash criptograficos (resumenes de mensajes)
- Los codigos de autenticacion de mensajes (MAC)

La mayoria del tiempo en esta parte nos basaremos en la criptografia simetrica usando la funcion mcrypt.

Almacenar contraseñas 

Nunca debemos almacenar nuestras contraseñas en texto plano, ya que puede contener un grave riesgo de seguridad, es por ello que podemos usar funciones hash seguidas de un salto, por ejemplo:



 <?php
/* $contra contiene la contraseña. */
$salto = 'xsW3#|-AsWGB';
$contra_hash = md5($salto . md5($contra . $salto));
/* almacena el hash contra. */
?>


Si los valores del hash son identicos entonces las contraseñas tambien lo son. Con este tipo de codigo no podemos recuperar las contraseñas, por lo que lo unico que hay que hacer es enviarle una nueva contraseña y actualizar la base de datos.


Usando mcrypt

La funcion estandar para la criptografia en PHP es mcrypt, y tiene un gran listado de compatibilidad con algoritmos, para verlos solo necesitamos usar la funcion mcrypt_list_algorithms():



 <?php
echo '<pre>' . print_r(mcrypt_list_algorithms(), TRUE) . '</pre>';
?>

Para cifrar y descifrar datos usamos las funciones mcrypt_encrypt() y mcrypt_decrypt(), cada una de ellas acepta 5 argumentos:


 <?php
 mcrypt_encrypt($algorithm,
 $key,
 $cleartext,
 $mode,
 $iv);
 mcrypt_decrypt($algorithm,
 $key,
 $ciphertext,
 $mode,
 $iv);
?>


La clave ($key) el segundo argumento es extremadamente sensible, debes tenerlo siempre en algun lugar seguro. Podriamos usar la tecnica que usamos en la parte 8 de seguridad en php, donde aplicabamos la clave de la base de datos. Un key hardware (clave de hardware) es mucho mas seguro, pero mucho mas costoso.

Se pueden utilizar numerosos modos, puedes usar la funcion  mcrypt_list_modes() para ver los modos disponibles:


 <?php
echo '<pre>' . print_r(mcrypt_list_modes(), TRUE) . '</pre>';
?>







El 5to argumento $iv es el vector de inicializacion, y es creado con la funcion mcrypt_create_iv():


 class crypt
{
 private $algorithm;
 private $mode;
 private $random_source;
 public $cleartext;
 public $ciphertext;
 public $iv;
 public function __construct($algorithm = MCRYPT_BLOWFISH,
  $mode = MCRYPT_MODE_CBC,
  $random_source = MCRYPT_DEV_URANDOM)
 {
  $this->algorithm = $algorithm;
  $this->mode = $mode;
  $this->random_source = $random_source;
 }
 public function generate_iv()
 {
  $this->iv = mcrypt_create_iv(mcrypt_get_iv_size($this->algorithm,
  $this->mode), $this->random_source);
 }
 public function encrypt()
 {
  $this->ciphertext = mcrypt_encrypt($this->algorithm,
  $_SERVER['CRYPT_KEY'], $this->cleartext, $this->mode, $this->iv);
 }
 public function decrypt()
 {
  $this->cleartext = mcrypt_decrypt($this->algorithm,
  $_SERVER['CRYPT_KEY'], $this->ciphertext, $this->mode, $this->iv);
 }
}


Esta clase se hace referencia en otros ejemplos, este es su uso:




 <?php
 $crypt = new crypt();
 $crypt->cleartext = 'This is a string';
 $crypt->generate_iv();
 $crypt->encrypt();
 $ciphertext = base64_encode($crypt->ciphertext);
 $iv = base64_encode($crypt->iv);
 unset($crypt);
 /* Almacena $ciphertext y $iv (incializa el vector). */
 $ciphertext = base64_decode($ciphertext);
 $iv = base64_decode($iv);
 $crypt = new crypt();
 $crypt->iv = $iv;
 $crypt->ciphertext = $ciphertext;
 $crypt->decrypt();
 $cleartext = $crypt->cleartext;
?>





 

Almacenamiento de numero de tarjetas de credito

Siempre que se almacenen tarjetas de credito se debe ser cuidadoso, existen unas reglas para mantener seguras nuestras tarjetas de credito, en wikipedia podemos ver al final en la seccion Security: http://en.wikipedia.org/wiki/Bank_card_number

 
En este caso veremos como almacenar una tarjeta de credito encriptando nuestros datos, para ello tambien usaremos la funcion base64_encode() ya que la cadena es binaria:

 <?php
 $crypt = new crypt();
 $crypt->cleartext = '1234567890123456';
 $crypt->generate_iv();
 $crypt->encrypt();
 $ciphertext = $crypt->ciphertext;
 $iv = $crypt->iv;
 $string = base64_encode($iv . $ciphertext);
?>


Esto lo guardamos en la base de datos y lo recuperamos de la siguiente manera:

 <?php
 $string = base64_decode($string);
 $iv_size = mcrypt_get_iv_size($algorithm, $mode);
 $ciphertext = substr($string, $iv_size);
 $iv = substr($string, 0, $iv_size);
 $crypt = new crypt();
 $crypt->iv = $iv;
 $crypt->ciphertext = $ciphertext;
 $crypt->decrypt();
 $cleartext = $crypt->cleartext;
?>
 
Esta implementacion asume un algoritmo y modo consistente. Si estos datos no son encodeados, usted debe almacenarlos para desencriptar los datos. La clave ($key) es la unica que se debe mantener en secreto.


Cifrar datos de sesion

Si piensas que los datos son particularmente sensibles si llegasen a ver los datos de tu base de datos, es posible cifrar los datos de sesion. Esto no es absolutamente necesario pero si piensas que es necesario puedes implementarlo. En la parte 8 de seguridad en php vimos como almacenar los datos de sesion en la base de datos utilizando la funcion session_set_save_handler(), veamos una pequeña modificacion en la forma en que inserta y recupera los datos:


 <?php
function _read($id)
{
 global $_sess_db;
 $algorithm = MCRYPT_BLOWFISH;
 $mode = MCRYPT_MODE_CBC;
 $id = mysql_real_escape_string($id);
 $sql = "SELECT data
 FROM sessions
 WHERE id = '$id'";
 if ($result = mysql_query($sql, $_sess_db))
 {
 $record = mysql_fetch_assoc($result);
 $data = base64_decode($record['data']);
 $iv_size = mcrypt_get_iv_size($algorithm, $mode);
 $ciphertext = substr($data, $iv_size);
 $iv = substr($data, 0, $iv_size);
 $crypt = new crypt();
 $crypt->iv = $iv;
 $crypt->ciphertext = $ciphertext;
 $crypt->decrypt();
 return $crypt->cleartext;
 }
 return '';
}
function _write($id, $data)
{
 global $_sess_db;
 $access = time();
 $crypt = new crypt();
 $crypt->cleartext = $data;
 $crypt->generate_iv();
 $crypt->encrypt();
 $ciphertext = $crypt->ciphertext;
 $iv = $crypt->iv;
 $data = base64_encode($iv . $ciphertext);
 $id = mysql_real_escape_string($id);
 $access = mysql_real_escape_string($access);
 $data = mysql_real_escape_string($data);
 $sql = "REPLACE
 INTO sessions
 VALUES ('$id', '$access', '$data')";
 return mysql_query($sql, $_sess_db);
}
?>


Con esto nuestros datos serian almacenados y leidos de forma segura. Hasta aqui se terminan las partes de seguridad en PHP espero que hayan disfrutado leyendo todo este tiempo.


Referencias:

* Essential PHP Security
* Wikipedia
* PHP documentacion oficial



No hay comentarios:

Publicar un comentario en la entrada