'' |
Encripta, que algo quedaPor Mrzo Varea
¿Para qué queremos encriptar? Evidentemente, para mejorar la seguridad. Es razonable que ciertos datos no estén inmediatamente accesibles al primer curioso que nos explore el disco duro a ver qué encuentra; por ejemplo, sueldos de empleados, nombres, direcciones y teléfonos de clientes, de contactos comerciales, de pacientes, de agentes del CESID... También, por supuesto, nombres y claves de acceso de usuarios autorizados de la aplicación.
La encriptación no es el único método de hacerlo. Un buen principio, por ejemplo, es un sistema operativo con buen control de acceso a ficheros y directorios. Si el sistema es monopuesto, como en los tiempos antiguos, el problema puede atacarse con dispositivos mecánicos, como llaves (pero hay que tener cuidado con ellas, no perderlas...) o cadenas. En una red la dificultad se incrementa exponencialmente con el número de puestos: lo más seguro será que no todo el que tenga acceso a uno esté autorizado a saberlo todo; y, cuantos más puestos haya, más fácil será que alguno quede temporalmente libre para uso de fisgones. Si la red tiene acceso al mundo exterior, circunstancia nada extraña, será ya imprescindible hacer algo al respecto.
Otro punto peligroso son las copias de seguridad. De nada sirve poner guardia de vista a los puestos de trabajo si los backups se guardan en un cajón cualquiera. Si se trata, en cambio, de una empresa prudente, que los almacena en una caja fuerte resistente al fuego y a otros azares, vamos por buen camino.
Hay que tener claro un principio general: la seguridad y la comodidad tienden a ser inversamente proporcionales. La seguridad total probablemente hará los datos inutilizables (“destrúyase antes de leerlo”); la posibilidad de acceder a cualquier dato inmediatamente desde cualquier parte es una pesadilla de seguridad. Cualquier sistema razonable es un compromiso entre ambas, nunca sin inconvenientes. Los procesos de cifrado/descifrado consumen tiempo (si cuesta 1 milisegundo por campo, eso significa 1 segundo por cada 1000 campos, lo que no es despreciable). Las claves deberían ser cuanto más largas mejor, y no fácilmente adivinables por un extraño: nada de usar el propio nombre o fecha de nacimiento, y tampoco los del cónyuge o el perro (¿habéis visto Rosalie va de compras?); pero si resultan tan difíciles de recordar que la gente se las va apuntando en papelitos que deja por ahí, la seguridad ha empezado a derrotarse a sí misma.
Si deseamos, pues, que quien acceda sin autorización a nuestros ficheros no pueda leerlos con toda sencillez, podemos, por ejemplo, cifrar los campos que nos convenga. Hay básicamente dos tipos de cifrarios: los de sustitución, en los que cada carácter se cambia por otro según ciertas normas, y los de transposición, en los que se cambian las posiciones de los caracteres. Combinarlos no es mala idea, por otra parte.
El cifrario de sustitución más antiguo de que se tiene noticia es el atbash, usado en el libro de Jeremías para referirse a Babilonia sin despertar las iras de los babilonios. En él, cada una de las 22 letras del alfabeto (hebreo, por supuesto) se sustituye por la que ocupa la posición opuesta: la primera (Aleph) por la última (Tau), la segunda (Beth) por la penúltima (Shin), etc. Una versión para ASCII podría ser la siguiente:
Si lo aplicamos a la cadena “Al corro de la patata” obtendremos “¾“ߜߛšß“žßž‹ž‹ž”, que, a primera vista, no parece mal. Para descifrar, se aplica la misma función a la cadena cifrada. Otro código con solera es el usado por Julio César. Sustituía la A por la D, la B por la E y así sucesivamente. Versión para ASCII, con un segundo parámetro para indicar la sustitución (lo convertimos así en un cifrario de rotación genérico): * JCesar.prg * * Devuelve una cadena en la que cada * carácter de Entrada ha sido sustituido * por el que está situado N lugares más * abajo en la tabla ASCII. parameters entrada, n private long, salida, car, i long = len(m.entrada) salida = [] for i=1 to m.long car = substr(m.entrada, m.i, 1) car = chr(mod(asc(m.car)+m.n), 256)) salida = m.salida + m.car endfor return m.salida Para descifrar una cadena cifrada, se vuelve a aplicar la función con el número clave cambiado de signo. Ahora, la mala noticia: los cifrarios de sustitución monoalfabéticos, como los descritos, son vulnerables desde la Edad Media. El método para forzarlos, según la frecuencia con que aparecen los distintos caracteres en un texto de un idioma dado, está bien descrito en El escarabajo de oro de Edgar Allan Poe; y cualquier criptólogo, aun aficionado, no los mirará sino con absoluto desprecio. De modo que tendremos que encontrar algo mejor; digamos un cifrario de sustitución polialfabético: ¿qué tal usar como clave una cadena de caracteres, de modo que, por ejemplo, sumemos a cada carácter de la cadena a cifrar el valor ASCII del carácter correlativo de la clave, volviendo al principio cuando si la clave se nos acaba? * Cifra1.prg * * Devuelve una cadena en la que cada * carácter de Entrada ha sido sustituido * por el que resulta de sumar a su valor * ASCII el del enésimo carácter de * Clave. * Si Factor tiene un valor cualquiera, * se restan los valores (para descifrar * una cadena cifrada). parameters entrada, clave, factor factor = iif(parameters()=3, -1, 1) private long, lclave, sumar, salida, ; car, i long = len(m.entrada) lclave = len(m.clave) dimension aClave[m.lclave] for i=1 to m.lclave aClave[m.i] = ; asc(substr(m.clave, m.i, 1)) ; * m.factor endfor salida=[] for i=1 to m.long car = substr(m.entrada, m.i, 1) car = chr(mod(asc(m.car) ; + aClave[mod(m.i,m.lclave)+1] ; , 256)) salida=m.salida + m.car endfor return m.salida Para descifrar, úsese la misma función con un tercer parámetro de cualquier tipo y valor. Si encriptamos con Cifra1 un campo que contenga los apellidos “Pérez López ” con la clave “Candilejas”, obtendremos ±WÖÎæ…Tã¨ÛŽ„‰Œ…Š“cŽ„‰Œ…Š“cŽ„‰Œ… Aquí un carácter ya no es sustituido siempre por el mismo, lo cual es una mejora. Obsérvese, sin embargo, que al final de la cadena se repite la secuencia “Ž„‰Œ…Š“c”. Naturalmente, se debe a los espacios en blanco del final del campo. Resulta de lo más molesto, pues por sí solo ya basta para que el enemigo descerraje el sistema de cifrado. Una posible solución es tener en cuenta la posición que ocupa cada carácter en la cadena; por ejemplo, sumándola al valor ASCII del carácter en cuestión. * Cifra2.prg * * Devuelve una cadena en la que cada * carácter de Entrada ha sido sustituido * por el que resulta de sumar a su valor * ASCII el del enésimo carácter de Clave * y la posición que ocupa en Entrada. * *(Igual que Cifra1 hasta aquí): salida = [] for i=1 to m.long car = substr(m.entrada, m.i, 1) car = chr( mod(asc(m.car) ; + aClave[mod(m.i, m.lclave) + 1] ; + m.i * m.factor, 256) ) salida=m.salida + m.car endfor return m.salidaExiste una biblioteca de dominio público, Cipher.plb (y .fll), escrita por Tom Rettig y Leonard Zerman, y adaptada para la API de FoxPro por Walt Kennamer, que contiene las funciones ENCRYPT y DECRYPT, con sustitución de los caracteres según clave (de al menos 3 caracteres de longitud) y posición, no por operaciones aritméticas sino de bits. Se incluyen en el diskette del mes las versiones para FoxPro DOS 2.0 y 2.5, y FoxPro Win 2.5 (las 2.5 funcionan también en 2.6), junto con sus fuentes en C. Permiten cifrar y descifrar cadenas de hasta 1024 caracteres. (Tenemos operaciones de bits disponibles en Visual FoxPro 5, con las funciones BitAnd(), BitOr(), BitXOr(), BitNot()... Pero, dado que FoxPro 2.6 aún se usa ampliamente, hemos preferido atenernos a código ejecutable también en esta versión). Si aplicamos a las mismas cadena y clave del caso anterior (“Pérez López ”, “Candilejas”) nuestra nueva función Cifra2, obtenemos una cadena sin repeticiones que den el chivatazo: ²YÙÒ닽\ì²æš‘—›•›“¦w–¤›¡¥Ÿ¥° ®¥«¯© Parece que ya podemos respirar tranquilos. Vamos a asegurarnos, por ejemplo con “Rodríguez Pi ”: ´ßËß^ÒæÎöm¼ã‘—›•›“¦w–¤›¡¥Ÿ¥° ®¥«¯© Los espacios en blanco del final de la cadena se cifran del mismo modo, lo que descubre el método y la clave. Para despistar un poco más, podríamos tal vez aplicar alguna transposición, barajando los caracteres de la cadena. Esto haría la tarea algo más larga, pero no arredraría a un criptoanalista serio, sobre todo si tiene una tabla de buen número de registros para trabajar: seguirían apareciendo los mismos caracteres en posiciones fijas. Si además dispone de los ficheros índice (o si el fichero está ordenado por un campo encriptado), la labor se le hará bastante más llevadera. Otro posible enfoque es usar una clave más larga, de modo que no importe mucho que quede traicionada la última parte. Lo malo es que sí importa, por varios motivos. En primer lugar, una clave larga es fácil de olvidar si no es fácil de recordar; por ejemplo, un fragmento de algún libro (“En un lugar de la Mancha de cuyo nombre no quiero acordarme, no ha mucho tiempo que vivía un hidalgo ... etc”). Si el descifrador consigue leer “ancha de cuyo nom”, nos hemos caído con todo el equipo. Y también es más dificililla de teclear a ciegas sin errores, pero no nos metamos en eso. En realidad, para muchos usos no es necesario recordar la clave, que puede ser aleatoria (o pseudoaleatoria), tan larga como queramos (óptimamente, tan larga como la cadena más larga que queramos encriptar), y estar almacenada en las profundidades de la aplicación, ya sea como cadena literal (“clave=[90ok ncvb9u)?odflknv 9’3qouKnkb]”) (¡no llamar “clave” a la clave!) o como un fichero .Mem incluido en el ejecutable (recuérdense las órdenes Save To y Restore From). Esto nos permite también que la aplicacion tenga precalculada la matiz con los valores ASCII de los caracteres de la clave, lo que nos ahorra tiempo al cifrar o descifrar. Pero, aun con una clave óptima, el descifrador dispone de muchos “mensajes” para analizar. Si los apellidos de longitud media vienen a ser “García Sánchez”, también habrá algún “Pi i Pi” y algún “Sainz de Varanda y Jiménez de la Iglesia”. La segunda mitad del último, aproximadamente, ya no es problema; si se tiene alguna idea acerca de quién se trata, el resto de la clave es historia también: un nombre de cliente a medio descifrar como “???????????????usiness Machines” ya está descifrado. Un modo de hacerlo más difícil sería tener en cuenta otra variable, como el número de registro; por ejemplo, sumándolo al valor ASCII de los caracteres: Car = chr( mod(asc(m.car) ; + aClave[mod(m.i,m.lclave) +1] ; + (m.i + recno()) * m.factor, ; 256)) Pero esto obliga a ser de lo más cuidadosos a la hora de crear registros y hacer un PACK o un SORT (¡aunque no conviene ordenar según un campo encriptado!): como se empleen esas órdenes de Fox por las buenas, nos habremos metido en un buen lío. Será necesario descifrar todo, ordenar o eliminar borrados, y entonces, cuando cada registro tenga ya su nuevo número, volver a cifrar lo que sea menester. Fácil y rápido :-) Nos evitaría este problema tener en cuenta, en lugar del número de registro, la historia anterior de la cadena; por ejemplo, la suma de todos los valores ASCII de los caracteres anteriores. Para cifrar: CodCar = Asc(SubStr(m.Entrada, m.i, 1)) Car = Chr( Mod(m.CodCar ; + aClave[Mod(m.i, m.lClave)+1] ; + m.Suma, ; 256) ) Suma = m.Suma + m.CodCar Salida = m.Salida + m.Car Y para descifrar: CodCar = ; Mod(Asc(SubStr(m.entrada, m.i, 1)) ; - aClave[Mod(m.i, m.lClave)+1] ; - m.Suma, ; 256) Suma = m.Suma + m.CodCar Salida = m.Salida + Chr(m.CodCar) Un modo de evitar los espacios finales sería, claro, usar sólo campos memo para la información que se desee encriptar. Pero el criptoanalista seguiría teniendo un montón de “mensajes” para hallar pautas. De todas formas, para detener a curiosos más o menos accidentales y asistemáticos, con lo dicho ya basta. Y tampoco sabría yo decir mucho más.
|