Índice de contenidos
Instalación en Windows
Instalación en Ubuntu
Servidores seguros
Páginas dinámicas
Sintaxis básica
Operaciones
Arrays
Formatos de presentación
Operadores
Bucles
Extraer y ord. información
Funciones
Ficheros externos
Imágenes dinámicas
Gestión de directorios
Cookies y sesiones
Clases y objetos
Ficheros en formato PDF
Bases de datos MySQL
PHP y XML
PDO - Bases SQLite / MySQL
MySQL a traves de misqli
Algo de JavaScript y AJAXComunicación segura
El objetivo de la comunicación segura es que la confidencialidad de información transmitida evitando también el riesgo de que pueda ser modificada o manipulada durante el proceso de intercambio. Por medio del protocolo SSL –Secure Socket Layer– (una de las opciones más populares) se puede ocultar a ojos de teceros la información que fluye, de forma bidireccional, entre el servidor y el cliente. Para lograr tales propósitos es necesario recurrir a alguno de procedimientos criptográficos que describiremos a continuación.
Cifrado simétrico
Supongamos que A y B convienen enviarse mensajes cifrados y acuerdan reemplazar las vocales por los números 1 al 5. De esa forma cuando uno de ellos pretenda escribir la palabra poetisa utilizará la grafía: p42t3s1. El receptor del mensaje podrá reemplazar los números por su vocal equivalente y recomponer el mensaje. Este sería un ejemplo muy simple de cifrado simétrico o con clave privada.

El grado de inseguridad de un sistema de cifrado simétrico está condicionado por dos factores. Uno de ellos, común a todos los sistemas de cifrado, es la mayor o menor resistencia que pueda presentar a los intentos de descubrir la clave a base de ensayo y error («fuerza bruta»). Normalmente, un aumento del tamaño de la clave representa un incremento la seguridad.
El otro problema, el más grave, es la falta de certeza sobre la identidad de remitente. No hay nada que nos de garantías sobre quien ha cifrado el documento. De todas formas veremos un poco más adelante como subsanar estos inconvenientes ya que este es el método de cifrado que se utiliza habitualmente cuando se navega por páginas seguras.
Existen varios algoritmos de cifrado simétrico. Uno de los más populares es el conocido como DES adoptado como un estándar, para comunicaciones no clasificadas, por el Gobierno de los EE.UU. en 1976. Su grado de eficiencia quedó muy cuestionado cuando, en 1998, Electronic Frontier Foundation logró fabricar una máquina capaz de descifrarlo en tres días. Esta circunstancia forzó la aparición de versiones mejoradas tales como el DES múltiple y otras que han logrado paliar en gran medida sus deficiencias iniciales y hacer de DES múltiple un algoritmo de uso bastante frecuente.
El algoritmo IDEA (International Data Encryption Algorithm) es otro de los que aperecen, en este caso en 1992, como opciones alternativas al uso de DES múltiple. No fué el único. En octubre de 2000 el National Institute for Standards and Technology (NIST) adoptó del algoritmo RIJNDAEL como nuevo Estándar Avanzado de Cifrado (AES) para su empleo en aplicaciones criptográficas no militares.
PHP5 dispone de funciones de cifrado para una gran variedad de algoritmos. Aquí tienes una lista de los algoritmos de cifrado que soporta actualmente.
Además de los distintos algoritmos de cifrado hay otro elemento diferenciador. Se trata de lo conocido como modo de cifrado. Los algoritmos de cifrado suelen trabajar con bloques de longitud fija. La cadena objeto del cifrado es descompuesta en bloques de igual tamaño y tratada de diferentes modos. En los llamados ECB (Electronic CodeBook) cada bloque es cifrado de forma independiente. En otros modos, por ejemplo el CBC (Cipher Block Chaining), a cada bloque de texto plano se le aplica una operación que requiere el uso del texto de bloques anteriores. En estos casos, para hacer cada mensaje único ha de utilizarse un vector de inicialización. Aquí tienes la lista de los modos de cifrado soportados por tu versión de PHP.
Funciones PHP de cifrado simétrico
Configuración de PHP
PHP dispone de funciones que permiten efectuar algunos tipos de cifrado. Antes de nada y como siempre en estos casos es conveniente comprobar en el fichero info.php si están activadas estas opciones.

En el caso de la versión PHP 5.3.6 para Windows esta opción viene activada por defecto. Igual ocurre en el caso de PHP 5.3 en Ubuntu 10.10. Por el contrario, en versiones anteriores de PHP es necesario, además de seguir el proceso de configuración que se indica en el enlace, descomentar de su fichero php.ini, quitando el punto y coma (;) que por defecto lleva delante la extension=php_mcrypt.dll.
Si en alguna versión de Ubuntu no viniera configurado por defecto podría instalarse ejecutando desde la consola el siguiente comando:
sudo apt-get install php5-mcrypt
Proceso de cifrado y descifrado
Los procesos de cifrado y descifrado mediante PHP son muy similares y requieren, en ambos casos, la siguiente secuencia de funciones:
$identificador=mcrypt_module_open(algoritmo, ubicacion, modo, directorio)Abre un nuevo módulo de cifrado identificado por el valor la variable $identificador. Requiere estos parámetros: algoritmo es una cadena que ha de contener, entre comillas, el nombre del algoritmo que pretendemos utilizar. Será uno de los nombres de la lista antes mencionada y que puedes ver nuevamente desde este enlace. El parámetro ubicacion permite especificar el directorio con la eventual ubicación del algoritmo de cifrado en el caso de que se usara uno distinto a los incluidos en la distribución de PHP. Lo habitual será que pongamos como valor una cadena vacía de la forma "". El parámetro modo será una cadena en la que se especifique el modo de cifrado que pretendamos utilizar y habrá de contener uno de los valores (escrito entre comillas) que tenemos en este enlace. Por último, el parámetro directorio será habitualmente una cadena vacía "" ya que está definida para establecer el modo del directorio eventualmente establecido en el parámetro ubicacion.
Iniciado el módulo de cifrado y según el modo de cifrado establecido puede resultar necesario crear un vector de inicialización cuya longitud se determina por medio de la función:
$longitud=mcrypt_enc_get_iv_size($identificador)en la que $identificador es el identificador de recurso establecido por la función anterior y $longitud la longitud de del mencionado vector de inicialización.
Conocida la dimensión $longitud del vector de inicialización (puede ser cero en el caso de que no sea requerido su uso) debemos crearlo invocando la función:
$vector=mcrypt_create_iv($longitud, constante)donde $longitud es resultado de la función anterior y constante es la constante MCRYPT_RAND que debemos incluir sin comillas. El resultado, $vector, será el vector de inicialización.
El paso siguiente será iniciar todos los buffers necesarios para el posterior proceso de cifrado o de descifrado. De esa labor se encarga la función:
mcrypt_generic_init($identificador, clave, $vector)en la que $identificador es el identificador del recurso, clave es una cadena que se usará como CLAVE DE CIFRADO y $identificador el identificador del recurso y $vector es el vector de inicialización.
El último paso ya será el cifrado o descifrado propiamente dichos. Si se trata de cifrar utilizaremos:
$cifrado=mcrypt_generic($identificador, texto)donde texto es una cadena (o variable) que contiene el texto que pretendemos cifrar y donde $identificador sigue siendo el identificador del recurso. El resultado del cifrado es recogido en la variable $cifrado
Si se trata de descifrar la función anterior debe ser sustituida por
$descifrado=mdecrypt_generic($identificador, $cifrado)siendo $cifrado la variable (ocadena) cifrada que pretendemos desencriptar y $descifrado el resultado de la desencriptación. Llegados a este punto, solo nos restaría liberar los buffer reservados para el cifrado y cerrar el recurso mediante las funciones:
mcrypt_generic_deinit($identificador)De esta forma se consigue un método de cifrado y descifrado aplicable con cualquiera de los algoritmos y modos de cifrado disponibles.
Ejemplos de cifrado y descifrado simétrico
Un ejemplo de como puede cifrarse con clave simétrica podría ser este:
<?php # establecemos la clave y la cadena a encriptar $clave = "clave"; $texto ="Esta es una cadena de prueba"; /*creamos un identificador de encriptado en el que indicamos el tipo de cifrador (cast-128) y el modo de cifrado (ecb) */ $ident = mcrypt_module_open('cast-128', '', 'ecb', ''); /* dado que algunas funciones requieren de un vector de inicializacion acorde con sus especificaciones esta función determina el tamaño de ese vector atendiendo al tipo de identificador */ $long_iniciador=mcrypt_enc_get_iv_size($ident); /* crea el vector de inicialización con valores aleatorios y dándole la dimensión precalculada en la función anterior */ $inicializador = mcrypt_create_iv ($long_iniciador, MCRYPT_RAND); /* hacemos algunas comprobaciones innecesarias para ejecutar el script. Simplemente son descriptores de algunas funciones complementarias */ /* comprobamos el tamaño maximo (en bytes) que puede tener la clave para este algoritmo de cifra*/ print "La clave no puede sobrepasar los "; print mcrypt_enc_get_key_size ($ident)." bytes<br>"; /* escribimos el tamaño del bloque del algoritmo que estamos usando*/ print "El tamaño del bloque de cifrado es "; print mcrypt_enc_get_block_size($ident)." bytes<br>"; print "El modo de cifrado es "; print mcrypt_enc_get_modes_name($ident)."<br>"; print "El algoritmo de cifrado es "; print mcrypt_enc_get_algorithms_name($ident)."<br>"; print "El tamaño del vector de inicialización es "; print mcrypt_enc_get_iv_size ($ident)."<br>"; /* Contimuamos la secuencia de encriptado incializando todos los buffer necesarios para llevar a cabo las labores de encriptado */ mcrypt_generic_init($ident, $clave, $inicializador); /* realiza el encriptado proopiamente dicho */ $texto_encriptado = mcrypt_generic($ident, $texto); /* libera los buffer pero no cierra el modulo */ mcrypt_generic_deinit($ident); /* esta instruccion es necesaria para cerrar el modulo de encriptado*/ mcrypt_module_close($ident); # imprimimos el resultado de la encriptación # en este caso añadimos una codificación de ese resultado en base 64 print "La cadena encriptada es: ".base64_encode ($texto_encriptado); /* guardamos la cadena encriptada en un fichero con nombre encriptado */ file_put_contents('encriptado',$texto_encriptado); print "<br> está codificada en base 64"; ?>
El proceso inverso, la desencriptación de una cadena codificada puede hacerse de la forma que se indica en el siguiente ejemplo.
<?php /* hemos de usar la misma clave con la que ha sido encriptado */ $clave = "clave"; /* leemos el fichero encriptado creado por el script anterior */ $texto_encriptado =file_get_contents('encriptado'); /*creamos un identificador de encriptado que ha de ser el mismo con el que hemos realizado la encriptación */ $ident = mcrypt_module_open('cast-128', '', 'ecb', ''); /* dado que algunas funciones requieren de un vector de inicializacion acorde con sus especificaciones esta función determina el tamaño de ese vector atendiendo al tipo de identificador anterior*/ $long_iniciador=mcrypt_enc_get_iv_size($ident); /* crea el vector de inicialización con valores aleatorios y dándole la dimensión precalculada en la función anterior */ $inicializador = mcrypt_create_iv ($long_iniciador, MCRYPT_RAND); /* incializa todos los buffer necesarios para llevar a cabo las labores de encriptado */ mcrypt_generic_init($ident, $clave, $inicializador); /* realiza el desencriptado proopiamente dicho. Realmente es la unica diferencia básica entre este script y el ejemplo anterior */ $desencriptado = mdecrypt_generic($ident, $texto_encriptado); /* libera los buffer pero no cierra el modulo */ mcrypt_generic_deinit($ident); /* esta instruccion es necesaria para cerrar el modulo de encriptado*/ mcrypt_module_close($ident); # imprimimos el resultado de la encriptación # en este caso añadimos una codificación de ese resultado en base 64 print $desencriptado; ?>
El protocolo de Diffie-Hellman
Una de las debilidades del cifrado con clave simétrica reside en la necesidad de que ambas partes intercambien su clave de cifrado de forma confidencial. Esa confidencialidad puede conseguirse, incluso en comunicaciones de forma no segura, mediante una técnica conocida como protocolo de Diffie-Hellman.
Números y sus raíces primitivas
Este protocolo requiere la utilización de dos números que llamaremos p y g. El número p habrá de ser un número primo, por lo general muy grande, mientras que g ha de ser una raíz primitiva de p. Sin abundar en aspectos demasiado técnicos podemos decir que g es raiz primitiva de p si el conjunto de los restos de dividir entre p cada de las sucesivas potencias de g desde 1 hasta p-1 (g1, g1, g2, g3, ... gp-1) contiene a todos los naturales {1,2,3,4..., p-1}.
Eso significaría que para cualquier entero b menor que p y una raíz primitiva g del número primo p, se puede encontrar un único exponente i tal que b = gi (mod p) donde 0 ≤ i ≤ (p-1). El exponente i se conoce como el logaritmo discreto o índice de b para la base g, mod p. Este valor se representa como indg,p(b).
En la tabla que tienes a continuación puedes ver el proceso de comprobación de raices primitivas desarrollado para dos supuestos. Observa que en el caso de g=13 los restos de las divisiones se repiten (lo cual significa que hay valores que no aparecen) y por tanto 13 no es raíz primitiva de 23. Por el contrario, al comprobar el comportamiento de g=7 puedes ver los restos son, de forma no ordenada, los números naturales comprendidos entre 1 y 22. Por tanto g=7 si es raíz primitiva de 23.
| Comprobación de las raíces primitivas | ||||||
| z | p=23, g=7 (7 ES raíz primitiva de 23) | p=23, g=13 (13 NO ES raíz primitiva de 23) | ||||
| gz | Cociente entero gz/p | Resto gz/p | gz | Cociente entero gz/p | Resto gz/p | |
| 1 | 7 | 0 | 7 | 13 | 0 | 13 |
| 2 | 49 | 46 | 3 | 169 | 161 | 8 |
| 3 | 343 | 322 | 21 | 2197 | 2185 | 12 |
| 4 | 2401 | 2392 | 9 | 28561 | 28543 | 18 |
| 5 | 16807 | 16790 | 17 | 371293 | 371289 | 4 |
| 6 | 117649 | 117645 | 4 | 4826809 | 4826803 | 6 |
| 7 | 823543 | 823538 | 5 | 62748517 | 62748508 | 9 |
| 8 | 5764801 | 5764789 | 12 | 815730721 | 815730719 | 2 |
| 9 | 40353607 | 40353592 | 15 | 10604499373 | 10604499370 | 3 |
| 10 | 282475249 | 282475236 | 13 | 137858491849 | 137858491833 | 16 |
| 11 | 1977326743 | 1977326721 | 22 | 1792160394037 | 1792160394036 | 1 |
| 12 | 13841287201 | 13841287185 | 16 | 23298085122481 | 23298085122468 | 13 |
| 13 | 96889010407 | 96889010387 | 20 | 302875106592253 | 302875106592245 | 8 |
| 14 | 678223072849 | 678223072847 | 2 | 3937376385699289 | 3937376385699277 | 12 |
| 15 | 4747561509943 | 4747561509929 | 14 | 51185893014090757 | 51185893014090739 | 18 |
| 16 | 33232930569601 | 33232930569595 | 6 | 665416609183179841 | 665416609183179837 | 4 |
| 17 | 232630513987207 | 232630513987188 | 19 | 8650415919381337933 | 8650415919381337927 | 6 |
| 18 | 1628413597910449 | 1628413597910431 | 18 | 112455406951957393129 | 112455406951957393120 | 9 |
| 19 | 11398895185373143 | 11398895185373132 | 11 | 1461920290375446110677 | 1461920290375446110675 | 2 |
| 20 | 79792266297612001 | 79792266297611993 | 8 | 19004963774880799438801 | 19004963774880799438798 | 3 |
| 21 | 558545864083284007 | 558545864083283997 | 10 | 247064529073450392704413 | 247064529073450392704397 | 16 |
| 22 | 3909821048582988049 | 3909821048582988048 | 1 | 3211838877954855105157369 | 3211838877954855105157368 | 1 |
Obtención e intercambio de claves
Intecambiados de forma pública dos números p y g tales que g es raíz primitiva de p, cada uno de los dos usuarios que pretenden encontrar una clave común y secreta genera sendos números XA=gx (mod p) y XB=gy (mod p) donde x e y son números cualesquiera (secretos, ya que no serán intercambiados), menores que p. Ambos usuarios intercambian sus números. «A» facilita a «B» su XA y «B» proporciona a «A» el valor XB.
Cada usuario eleva a su clave secreta el valor público recibido del otro usuario y obtiene el resto de dividir el resultado entre el número p. Es decir, el usuario «A» efectúa la operación: KA=(XB)x (mod p) mientras que «B» calcula: Kb=(XA)y (mod p). Ambos resultados son iguales dado que se verifica que:
KA= (XB)x (mod p)= (gy)x (mod p)=(gyx)(mod p), y también que:
KB= (XA)y (mod p)= (gx)y (mod p)=(gxy)(mod p)
confirmádonse por tanto que ambos resultados han de ser iguales y que su valor será la clave simétrica buscada.
La práctica imposibilidad de que un tercero pueda obtener la clave radica en el hecho de que para calcularla es necesario conocer, al menos, uno de los números que hemos llamado secretos (x ó y) y aunque es cierto que pueden ser conocidos XA, XB, p y g (se transmiten o pueden transmitir de forma no segura) para romper la clave sería preciso buscar la solución a XA=gx (mod p) (logaritmo discreto de XA para la base g módulo p) tarea de muy alta dificultad (nada resulta imposible) cuando se trata de valores de p muy grandes. En esa dificultad radica precisamente la robustez de este protocolo desarrollado en 1976 por Whitfield Diffie y Martin Hellman.
Este es un ejemplo del procedimiento de cálculo de una clave de cifrado simétrico mediante el protocolo de Diffie-Hellman.
| Obtención de una clave común y secreta para cifrado simétrico | |
| Usuario «A» | Usuario «B» |
| Acuerdan, de forma pública, un
número y una de sus raíces primitivas clave y un generador. Establezcamos esos números como: p=23 y g=7 | |
| Elige un número natural secreto (puede hacerlo al azar) | Elige un número natural secreto (puede hacerlo al azar) |
| 2 | 13 |
| Eleva g al numero elegido, lo divide entre p y determina el resto de esa división | Eleva g al numero elegido, lo divide entre p y determina el resto de esa división |
| Resto de 72 entre 23 =3 | Resto de 713 entre 23 =20 |
| Envía el número anterior a «B» | Envía el número anterior a «A» |
| Eleva el valor recibido (20) a su clave secreta y calcula el resto de dividir ese resultado entre p (23). | Eleva el valor recibido (3) a su clave secreta y calcula el resto de dividir ese resultado entre p (23) |
| Resto de 202 entre 23=9 | Resto de 313 entre 23=9 |
| Ambos obtienen el mismo resultado. Ese valor, solo conocido por ellos, será la clave de cifrado simétrico | |
La identidad del comunicante como problema de seguridad
Admitiendo la invulnerabilidad (que nunca lo es del todo) del protocolo de Diffie-Hellman y la robustez de los algoritmos de cifrado simétrico tendríamos una más que aceptable seguridad del carácter confidencial (e incluso la integridad) de la información intercambiada. Pero queda en el aire una pregunta muy importante. ¿Qué garantías nos ofrecen estas técnicas de que los comunicantes son los que dicen ser? ¿Quien nos garantiza que no han sido suplantadas sus identidades? ¿Podemos evitar que uno de los comunicantes pueda negar, aún habiéndolo hecho, haber enviado una determinada información?. Por el momento la respuesta es no. Serán necesarios otros recursos para solventar ese problema. Tenemos pendiente resolver la autentificación ( que cada parte de la comunicación pueda asegurarse de que la otra parte es realmente quien dice ser) y el no repudio (permite a cada uno de los comunicantes probar de forma fehaciente que el otro ha participado en la comunicación de forma que el remitente del mensaje no pueda negar haberlo enviado o (caso de no repudio de destino), el destinatario del mensaje no puede negar haberlo recibido.
Para resolver esos problemas hemos de recurrir a procedimientos bastante similares a los de la vida cotidiana. Lo primero de todo sería firmar la información de la misma forma que se firma un cheque, una carta, un certificado o una solicitud. Esa acción no sería otra cosa que firmar digitalmente nuestros mensajes.
Ni en la comunicación cotidiana ni en la digital resulta de suficiente garantía la firma de un documento. Todos sabemos que puede ser más o menos hábilmente imitada. La firma manuscrita puede ser falsificada. Necesitamos algún tipo de garantía más.
Cuando en un centro de enseñanza se expide un certificado lo habitual es que vaya con la firma del secretario, el visto bueno del director y el sello del centro. Es decir, la firma del director es una garantía de la fidelidad de la del secretario.
Puede que no confiemos lo suficiente en la firma del director –Autoridad Certificadora– y que demandemos la legitimación notarial de la firma del secretario. La única diferencia sería el mayor rango de la Autoridad Certificadora. Desde luego siempre podemos seguir desconfiando. De estos asuntos trataremos en los temas siguientes.