[Ir al menú de PHP]
Encriptando código

¿Estoy buscando el nirvana?

Hace algunos días un amable comunicante me planteó una duda metafísica -que me produjo un cierto sonrojo- al preguntarme sobre la utilidad de los operadores bit a bit que tengo comentados en estas «Memorias» y me lanzó el guante de intentar ejemplificarlos con alguna aplicación práctica.

Mi respuesta inicial fué un simple ni p... idea. Rebusqué por la red y solo encontré alusiones a su potencial efectividad a la hora de hacer operaciones aritméticas (multiplicaciones básicamente) muy repetitivas ya que permitían operar a nivel de código máquina y al parecer ese proceso es mucho más rápido.

Hace algunos días... leía -por enésima vez- en un foro la pregunta siguiente: ¿como puedo encriptar el código PHP?.

Mi peculiar y tradicional osadía -la osadía del ignorante- me puso a elucubrar en un intento combinar ambos conceptos... y tratando de inventar algo que probablemente sea sólo un invento del T.B.O.

Es posible que a mi paisano -Juanín Carbayu- (si es que lee esto) pueda ...faerle un poco más la risa... y puede también que le reitere su apreciación de parecerle... un poco "aprovechado" el esconderse bajo esa excusa de titulo en caso de que a alguien pudiera parecerle mal... pero... la pura verdad es que el contenido de este apartado de mis «Memorias» es puramente experimental y por tanto carente de garantías sobre eficiencia.

Si te decides a probarlo... debes tener muy claro... que todo esto no tiene ninguna garantía de eficiencia... aparentemente funciona pero solo he experimentado con muy pocas páginas... y puede tener fallos -incluso graves- que pueden habérseme escapado.

Avisad@ quedas...

Un primer intento fallido

Aquí a la derecha tienes los scripts de un primer intento... que resultó -como era de esperar- fallido.

También tienes aquí -a la derecha- los fallos que yo he detectado pero que no significa que sean los únicos...

Una tabla de números aleatorios

Crear una tabla de números aleatorios no era excesivo problema... pero tenía un par de dudas muy importantes:

Sabía que una vez creada, no era problema ocultarla... bastaría guardarla en la zona privada del servidor... pero el problema era el tamaño y las repeticiones.

Inicialmente creé una tabla grande -de 10.000 valores- aunque más tarde me di cuenta de el tamaño no importaba demasiado, que podría ser variable y que eso probablemente no afectaría a la seguridad sino que quizás podría... hasta mejorarla.

Descarté también, el filtrado de la tabla para evitar repeticiones de valores porque pensé... que también podría añadir seguridad... si un número se repetía 3 veces ese bit... podría invertirse tres veces... y eso, quizá ralentizara el proceso pero... aparentemente no restaba seguridad sino que la añadía. ¿Estaré en lo cierto?

El proceso de encriptación

En este segundo intento lo único que hice modificar el script anterior.

Después de leer el fichero e invertir los bits de todos los caracteres de la cadena tenía que desinvertir (volver a invertir) aquellos caracteres cuya posición en la cadena coincidiera con los valores que tenía guardados en la tabla de números aleatorios.

Pero claro... los ficheros tienen longitud variable y mi tabla de números aleatorios ya estaba generada -con 10.000 valores, tal como te comenté más arriba- y el problema era... ¿cómo no restringir la aleatoriedad a esos primeros 10.000 caractéres?.

Fué ahí donde opté por crear el bucle que tienes en el ejemplo -aquí a la derecha- y al hacerlo y ver que funcionaba me dí cuenta de que con ese bucle podría utilizar una tabla de números aleatorios de una dimensión cualquiera, ya que -fuera el que fuera el tamaño del fichero a encriptar- podría hacer esas modificaciones aleatorias a lo largo de toda la cadena.

Lo probé... y funcionó -o al menos me pareció que funciona- aunque sigo dudando por lo que te sugiero -tal como de dije más arriba- que lo tomes con todas las reservas y dudas que vengan al caso en lo relativo a su eficiencia.

¡¡No quiero... engañarte...;-)

El proceso de desencriptación

La desencriptación no me planteó ningún problema.

Era cuestión de repetir exactamente el mismo proceso -esta vez sobre el fichero previamente encriptado- y en buena lógica el resultado esperable era obtener el fichero original. ¡¡Y así fué... !! O al menos, así pareció ser en las primeras pruebas... pero claro... aún me quedaba un problema conocido y quizá otros muchos desconocidos que en este momento ignoro cuales son...

Pero bueno... tal como decía Machado ... se hace camino al andar... y eso fué lo que hice... intentar solventar el problema conocido que no era otro que el que ya conoces... lograr que el script descodificado se ejecutara en el servidor antes de enviarlo al navegador... y hasta ahora solo conseguía enviar el fichero al navegador tal cual lo cual no era demasiado útil.

El tercer intento

En un principio pensé que la solución sería fácil. Pensaba que bastaría guardar la salida del script de descodificación en fichero y pensé que el directorio temporal del servidor podría ser un buen sitio por aquello de su condición de temporalidad... pero... una cosa piensa el borracho... y otra muy distinta el tabernero... y ocurrió que no había contado con que la configuración del PHP en mi servidor safe mode=on me impedía escribir en ese directorio.

Ni sabía, ni aún sé ... y lo que aún es peor no se si es posible como forzar a que se reejecutara el script una vez desencriptado.

La única solución que se me ocurría era escribir en el servidor ese fichero desencriptado y después llamarlo... pero claro... de poco me servía escribirlo y ejecutarlo... si no conseguía borrarlo inmediatamente después.

A base de experimentos, llegué a esto que -no me preguntes por qué- parece que funciona.

Si iniciaba una sesión al ejecutar el desencriptador podría recoger en una variable el identificador de sesión y utilizarlo para asignar nombre a ese fichero que pretendía crear, ejecutar y borrar.

Luego se me ocurrió que al escribir ese fichero de vida efímera podría incluir -al final del propio contenido del fichero- una script que realizara el autoborrado

Lo recogí en la variable llamada borrador) que encadené detrás del contenido del fichero para que apareciera como última línea de este fichero de vida efímera.

Una vez creado el fichero... era cuestión de llamarlo (se me ocurrió propagar la sesión en esa llamada) y si todo funcionaba... daba la impresión de que había resuelto mi problema...

Y... al menos aparente y creo que de forma milagrosa ... funcionó...

Faltaría ahora, mover la tabla de números aleatorios a un lugar más seguro del servidor y resolver el problema de los impacientes, porque claro... todo está muy bonito... pero... ¿qué pasa si actualizo la página? Ya no existe... ya ha sido borrada... y seguramente dará un error.
Un nuevo problema ... con el que no había contado...

¿Cómo puedo ser tan bruto?

Pues... si... tengo la sensación de mis orejas miden algo así como metro y medio... Señor, Señor... llevo una diciéndome a mí mismi: «yes un pollinín» (algo así como eres un burro, pero ... en bable y cariñosamente ;-)

Sencillamente me había olvidado de que PHP tiene una hermosa función llamada include que -curiosamente- vengo utilizando asiduamente a lo largo de estas «Memorias» y ni tan siquiera se me ocurrió pensar en ella.. Además de corroborar mi torpeza, acabo de comprobar aquel famoso axioma que dice: ¿por qué hacer las cosas fáciles si se pueden complicar?

El caso es que -olvidate de insertar cosas en el fichero de vida efímera- parece que basta desencriptar el fichero, crear ese temporal, llamarlo con un include para que se ejecute dentro del propio script de desencriptación... y luego borrarlo con unlink.

Ahora... ya no hay problema ninguno con el botoncito de Actualizar ni con el F5 ya que al recargar la página se ejecutaría nuevamente la desencriptación, etc. etc...

Pero... sigo en el mar de la duda... ¿cuantos problemas más tendrán estos scripts?... ¿cuanto les faltará aún por crecer a mis orejas?

Dificultando la lectura del código fuente

Tal como comento al margen, entiendo que la encriptación de los scripts de PHP puede tener justificación en el sentido de proteger aplicaciones de accesos malintencionados, pero no comparto la idea de ocultar el código fuente de las páginas que se envían al navegador del cliente.

Como puro ejercicio de gimnasia mental he intentado especular un poco con esa posibilidad y el resultado es el script que tienes un poco más abajo...a la derecha.

Se trataría de convertir la salida hacia el navegador del cliente en un JavaScript que descodificara y escribiera en el documento del cliente el contenido de una cadena transferida en formato cifrado según el standart RFC1738.

Esta codificación, similar a la que utiliza en método GET para el envio de formularios -que utiliza en método enctype= application/x-www-form-urlencoded- no es otra cosa que convertir los carácteres en secuencias %HH donde HH es el código hexadecimal del carácter.

Existen dos funciones en PHP que permiten hacer esa codificación:

urlencode (cadena)

que realiza esa codificación pero que... codifica los espacios como signos +, razón por la cual no es eficaz para nuestros propósitos.

rawurlencode (cadena)

que parece evitar el problema de la función anterior.

Si se transmite la información codificada en ese formato es necesario activar los mecanismos necesarios para que sea interpretada por el navegador, y ese mecanismo, parece llamarse JavaScript

La función unescape (cadena) de JavaScript parece ser capaz de descodificar las cadenas codificadas con la función rawurlencode y si se le combina con el método write del objeto document del navegador (también de JavaScript) lo que ocurrirá será que se escribirá -después de ser tratado por unuescape- el contenido de la cadena en la ventana del navegador mientras que al visualizar el código fuente solo se verá la etiqueta del javascript conteniendo la cadena codificada, con lo cual... veríamos el código de la página con forma... digamos que de lectura dificultada.

Por tanto, la salida enviada al navegador contendría lo siguiente:

<script language="JavaScript">

document.write(unescape(cadena));

</script>

Donde cadena sería el resultado de aplicar la función rawurlencode a la salida generada por PHP.

Si ejecutas este ejemplo que tienes aquí a la derecha y tratas de visualizar el código fuente podrás observar el resultado... caracteres raros, etc. etc. pero... no demasiado secretos porque... has yo sería capaz de traducirlos al cristiano sin demasiados problemas ;-)


 
 


Invirtiendo bits... primer intento

Mi primera elucubración fue esta: Si invierto los bits de cada uno de los caracteres de un fichero obtendré una cadena ilegible.


<? 
$fichero="index1.php";
# bufferizamos la salida para que no sea visible
# en el navegador del cliente
ob_start();
# abrimos el fichero en a "encriptar"
# en modo "lectura"
$f1=fopen($fichero,"rb");
# leemos el contenido integro del fichero
fpassthru($f1);
# recogemos el contenido del buffer
# en la variable cadena
$cadena = ob_get_contents();
# acabamos la bufferización
# y borramos el buffer
ob_end_clean();
# invertimos los "bits" de cada
# uno de los caracteres de la cadena
# que contiene el fichero original

$cd=~$cadena ;

# guardamos la cadena invertida
# en un nuevo fichero... que sería el fichero "encriptado"

$f1=fopen("index1_cd.php","w");
fwrite($f1,$cd);
fclose($f1);
?>

Ahora necesitamos un script que haga el proceso inverso... que lea el fichero y lo desencripte y que podría ser algo como esto:


<? 
$fichero="index1_cd.php";
# bufferizamos la salida para que no sea visible
# en el navegador del cliente
ob_start();
# abrimos el fichero en a "desencriptar"
# en modo "lectura"
$f1=fopen($fichero,"rb");
# leemos el contenido integro del fichero
fpassthru($f1);
# recogemos el contenido del buffer
# en la variable cadena
$cadena = ob_get_contents();
# acabamos la bufferización
# y borramos el buffer
ob_end_clean();
# invertimos los "bits" de cada
# uno de los caracteres de la cadena
# que contiene el fichero encriptado
# y que harían la desencriptación

$cd=~$cadena ;
y enviaríamos la cadena desencriptada a navegador
echo $cd;

?>

Esta primera experiencia, presenta -al menos- dos fallos de considerable bulto.

Complicando la codificación

Se trataba de conseguir que la inversión de bits no fuera lineal (aplicada sin más a todos y cada uno de los caracteres del fichero) sino que se produjera de forma aleatoria a lo largo del documento, de manera que unos bits aparecieran invertidos en el documento encriptado y otros aparecieran tal cual para eso... pensé que lo mejor sería... crear una tabla de números aleatorios.

La hice así...


<? 
for($i=1;$i<10000;$i++){
	$v=mt_srand((double)microtime()*1000000);
$matriz[$i]=mt_rand(1,10000);
}
sort($matriz);
$f1=fopen("claves_beta","a");
foreach($matriz as $c){
fwrite($f1,$c."\r\n");
}
fclose($f1);
?>

Ahora tocaba construir el script de encriptación utilizando la tabla de números aleatorios.

La codificación la hice así:


<? 
# incio la buferización
ob_start();
# abro el fichero "a encriptar"
# en modo lectura
$f1=fopen("index1.php","r");
# escribo el contenido
# en el buffer de salida
fpassthru($f1);
# recojo el buffer el
# la variable cadena
$cadena = ob_get_contents();
# cierro el buffer y lo borro
ob_end_clean();
# invierto todos los bits de la cadena
# que contiene el fichero "a encriptar"
$cd=~$cadena ;
# inicio una nueva buferización
# para leer el fichero de números aleatorios

ob_start();
$f1=fopen("claves_beta","r");
fpassthru($f1);
$claves=ob_get_contents();
ob_end_clean();
# convierto la cadena en una matriz
# utilizando como "separador" el salto de línea
# que había incluido detrás de cada número aleatorio
# al generar el fichero aleatorio
$matriz=explode("\r\n",$claves);
# determino el tamaño de la matriz
# de números aleatorios
$num_claves=sizeof($matriz);
# comparo la longitud de la cadena que
# contiene el el fichero (ya con los bits invertidos)
# con el tamaño de la matriz de números aleatorios
# y determino el número de pasos que debo dar para la encriptación
$pasos=((int)(strlen($cd)/$num_claves));
# diseño un bucle
# que recorrerá la cadena e invertirá aquellos caracteres
# cuya posición coincida con los valores de la tabla
# de numeros aleatorios,el bucle for garantiza
# que se recorrerá el fichero completo
for ($i=0;$i<=$pasos;$i++){
	foreach($matriz as $cambia){
		if ( $i*$cambia<strlen($cd) ){
		# extraigo un caracter
		$inv=substr($cd,$i*$num_claves+$cambia,1);
		 # lo reemplazo por otro con los bits invertidos
	    substr_replace($cd,~$inv,$i*$num_claves+$cambia,1);
			}
    }
}
# ahora guardamos la cadena codificada
# en un fichero que será la página "encriptada"
$f1=fopen("index1_clz.php","w");
fwrite($f1,$cd);
fclose($f1);
?>



Ver index1_clz.php encriptado


Y para descodificar, prácticamente lo mismo... solo era cuestión de abrir ahora el fichero encriptado y después mandarlo... al navegador.


<? 
ob_start();
$f1=fopen("index1_clz.php","r");
fpassthru($f1);
$cadena = ob_get_contents();
ob_end_clean();
$cd=~$cadena ;
ob_start();
$f1=fopen("claves_beta","r");
fpassthru($f1);
$claves=ob_get_contents();
ob_end_clean();
$matriz=explode("\r\n",$claves);
$num_claves=sizeof($matriz);
$pasos=((int)(strlen($cd)/$num_claves));
for ($i=0;$i<=$pasos;$i++){
foreach($matriz as $cambia){
	if ( $i*$num_claves+$cambia<strlen($cd) ){
		$inv=substr($cd,$i*$num_claves+$cambia,1);
		substr_replace($cd,~$inv,$i*$num_claves+$cambia,1);
		}
   }
}
echo $cd;
?>





Ejecutar la desencriptación

Creando un fichero con autoborrado

Aquí tienes la modificación del script de desencriptado con la que aparentemente solvento los problemas que te mencioné anteriormente... Este fichero que he utilizado como conejillo de Indias contiene includes y también código php que debe ejecutarse en el servidor...


<?  
session_start();
$id=session_id();
$cadena="";
$pag="php32_xlz.php";
$f1=fopen($pag,"rb");
while (!feof($f1)) {
    $cadena .= fgets($f1, 1024);
  }
fclose($f1);
$cd=~$cadena;
ob_start();
$f1=fopen("claves_beta","r");
fpassthru($f1);
$claves=ob_get_contents();
ob_end_clean();
$matriz=explode("\r\n",$claves);
$num_claves=sizeof($matriz);
$pasos=((int)(strlen($cd)/$num_claves));
for ($i=0;$i<=$pasos;$i++){
	foreach($matriz as $cambia){
		if ( $i*$num_claves+$cambia<strlen($cd) ){
		$inv=substr($cd,$i*$num_claves+$cambia,1);
		substr_replace($cd,~$inv,$i*$num_claves+$cambia,1);
		}
}
}

$borrador="<? unlink('".$id.".php'); ?>";
$f1=fopen("$id.php","w");
fwrite($f1,$cd.$borrador);
fclose($f1);

$vete=$id.".php?".session_name()."=".session_id();
Header("Location:$vete"); 
?>


Ver fichero encriptado Ejecutar el script anterior

¿Será la definitiva?


<?  
session_start();
$id=session_id();
$cadena="";
$pag="php32_xlz.php";
$f1=fopen($pag,"rb");
while (!feof($f1)) {
    $cadena .= fgets($f1, 1024);
  }
fclose($f1);
$cd=~$cadena;
ob_start();
$f1=fopen("claves_beta","r");
fpassthru($f1);
$claves=ob_get_contents();
ob_end_clean();
$matriz=explode("\r\n",$claves);
$num_claves=sizeof($matriz);
$pasos=((int)(strlen($cd)/$num_claves));
for ($i=0;$i<=$pasos;$i++){
	foreach($matriz as $cambia){
		if ( $i*$num_claves+$cambia<strlen($cd) ){
		$inv=substr($cd,$i*$num_claves+$cambia,1);
		substr_replace($cd,~$inv,$i*$num_claves+$cambia,1);
		}
}
}

$f1=fopen("$id.php","w");
fwrite($f1,$cd);
fclose($f1);

include("$id.php");
unlink("$id.php"); 
?>


Ejecutar con el include famoso

Mi duda metafísica

Llegado a este punto... me asaltan serias dudas... ¿será este un método válido para encriptación de ficheros?... ¿tendrá un montón de fallos que no he sabido ver?... ¿habré estado quemando pólvora en salvas?... Sinceramente... no lo sé... aquí te lo dejo para que lo pruebes si te apetece... abierto a sugerencias y críticas porque de lo único que estoy seguro es de que al menos ha sido un buen ejercicio de gimnasia mental... de eso es de lo único que estoy seguro... ;-)

¿Paranoia encriptadora?

He vivido en carne propia los efectos de aquella antigua obsesión de supervivencia profesional basada en el secretismo

Esa conducta -a mi juicio mezcla de inseguridad personal y sentimiento de inferioridad- se daba con mucha frecuencia (al menos yo la he vivido) en los años 60 - 70 (del siglo pasado... ¡¡qué viejo soy...;-)!!) y creo que afortunadamente hoy está superada, o al menos lo está en una gran medida.

En los comienzos de mi vida profesional -bisoño e ignorante- tuve la oportunidad de vivir el cripticismo de algunos y la tranparencia generosa de un gran hombre que es hoy es un gran amigo.

Me contrataron para realizar tareas de mantenimiento de lo que entonces era una maravilla de la tecnología... un equipo de fotocomposión - Linofilm Quick, para más señas- fabricado por Mergenthaler Linotype Co.

Mi escasos conocimiento de electrónica eran puramente teóricos y jamás me había enfrentado a la dura realidad de las urgencias empresariales ni tampoco a realidad práctica de la tecnología.

Vamor que andaba yo... más perdido que Adán el día de la madre. Pero mi amigo (Carlos Aurelio Valledor Santos) -un excelente linotipista pero totalmente profano en temas de electrónica- tenía una libreta negra.

Era su manual de supervivencia, pues en ella tenía cuidadosamente anotadas todas las incidencias que se habían producido en el famoso equipo y tenía anotada también... la forma en la que habían sido subsanadas

La generosidad de Carlos (¡¡gracias Charlie... de todo corazón!!) al poner a mi disposición su libreta negra me salvó de un montón de apuros y me salvó también... de un merecido despido.

He tratado siempre de seguir el ejemplo de mi amigo Charlie, que no es otro que tratar de facilitar a todo el mundo la poca información de la que dispongo y agradecer siempre la que recibo.

Por esa convicción, no comulgo con la idea de ocultar el código fuente de las páginas web ese código que tantas veces nos sirve para aprender un poco.

Una vez puesta de manifiesto mi opinión al respecto aquí tienes un ejemplo de un script que no solo descodifica los scripts de php, sino que también los envía al navegador del cliente en una forma un poco atípica... yo diría que más que con el código fuente encriptado... sería más correcto decir... con lectura dificultada.


<?  
session_start();
$id=session_id();
$cadena="";
$pag="php32_xlz.php";
$f1=fopen($pag,"rb");
while (!feof($f1)) {
    $cadena .= fgets($f1, 1024);
  }
fclose($f1);
$cd=~$cadena;
ob_start();
$f1=fopen("claves_beta","r");
fpassthru($f1);
$claves=ob_get_contents();
ob_end_clean();
$matriz=explode("\r\n",$claves);
$num_claves=sizeof($matriz);
$pasos=((int)(strlen($cd)/$num_claves));
for ($i=0;$i<=$pasos;$i++){
  foreach($matriz as $cambia){
	if ( $i*$num_claves+$cambia<strlen($cd) ){
	   $inv=substr($cd,$i*$num_claves+$cambia,1);
		substr_replace($cd,~$inv,$i*$num_claves+$cambia,1);
	}
   }
}
$f1=fopen("$id.php","w");
fwrite($f1,$cd);
fclose($f1);
# activamos la buferización para evitar que
# los resultados de la ejecución del include
# sean enviados hacia el navegador
ob_start();
include("$id.php");
$salida=ob_get_contents();
ob_end_clean();
# una vez recogido en la variable
# salida el resultado de la ejecución del script
# desactivamos la bufferización y borramos el buffer

# escribimos el script de JavaScript
# e incluimos dentro de el la página 
# codificandola con la funcion rawurlencode

$paranoia="<script language='JavaScript'>document.write(unescape('";
$paranoia .=rawurlencode($salida);
$paranoia.="'));</script>";
# enviamos el resultado al navegador
echo $paranoia;
# borramos el fichero del servidor
unlink("$id.php");
?>


Con código fuente de lectura dificultosa

Sugerir a un/a amig@ Envíame tus comentarios
Anterior
Indice
Siguiente