Spanish

warning: Creating default object from empty value in /home/gwolf/drupal6/modules/taxonomy/taxonomy.pages.inc on line 33.

Identidad y reputación en línea: Esquemas de confianza

Submitted by gwolf on Tue, 03/22/2011 - 18:57
Wolf G.  2011.  Identidad y reputación en línea: Esquemas de confianza. Software Gurú. 31:48-49.

Querido amigo: Soy la señora Mariam Abacha1, viuda del finado General Sani Abacha, ex-jefe de gobierno de Nigeria. (…) Para salvar a mi familia de una total bancarrota, estoy buscando transferir el total de US$24,000,000 a través de una institución bancaria confiable. (…) Como pago por su ayuda, le ofrezco el 30% de lo que podamos rescatar de la fortuna de mi querido esposo.

El tema conducente del presente ejemplar de SG, Pagos en línea, cruza necesariamente por el tema de los esquemas de establecimiento de reputación en línea. Cada vez menos gente asume confiable cualquier dato que encuentra en Internet sencillamente por estar ahí. Un logro del que puede enorgullecerse la comunidad de expertos que apuntan a la necesidad de concientización en nuestro quehacer en red es que la generalidad de los usuarios, por lo menos, ya desconfía cuando le piden datos para tener acceso a su dinero. Sin embargo, ¿qué es lo que nos lleva a confiar en determinados proveedores?

El problema de establecer la reputación de un tercero puede presentarse como un muy interesante ejercicio académico, con anclas en muy diversas áreas del conocimiento, desde las ciencias sociales hasta las matemáticas.

En un plano mucho más aplicado, todo el problema de la reputación puede resumirse en las preguntas, ¿Puedo confiar en que la contraparte es quien dice ser?, y ¿Puedo confiar en que dice la verdad?. Enfocándonos a las aplicaciones actuales, podemos principalmente traducir estas preguntas en:

Confianza en la identidad

Seguramente habrán recibido alguna vez un correo similar a aquel cuyas primeras líneas reproduje. Afortunadamente, es poca la gente que cae en estos esquemas2. Lo primero que debe venir a nuestra mente es, ¿estoy realmente intercambiando correo con la Sra. Abacha?

Hemos aprendido a desconfiar de la identidad de los extraños. Y cuando un extraño nos propone una transacción económica, nuestra primer reacción es desconfiar. Cuando efectuamos transacciones a través del navegador, nos hemos acostumbrado a buscar indicaciones de que estemos hablando con un servidor seguro. ¿Qué es esto? ¿Cómo lo valida el navegador?

Más allá de aplicar el sentido común, hay dos esquemas principales que nos permiten confiar la identidad de una entidad –individuo o empresa– con la que podamos tener un intercambio que incluya información confidencial (que requiera mantenerse a resguardo de terceros, como el número de nuestra tarjeta de credito) o no-repudiable (que nos interese tener un comprobante de haber realizado determinada transacción¸ sea pública o privada, con la persona o entidad en cuestión; lo que se ha dado por llamar firma electrónica): El esquema centralizado, basado en autoridades certificadoras (CAs) y firmas corporativas, y el esquema descentralizado, basado en llaveros de confianza y firmas personales. Ambos están basados en la criptografía de llave pública, con implementaciones derivadas de la criptografía de llave pública. No profundizaré en cómo estos pueden utilizarse para el intercambio de información, sino sobre la metainformación: Cómo apuntan a la confiabilidad sobre la identidad de un actor.

Por un lado, tenemos a la infraestructura de llave pública (PKI). Este es el esquema que siguen los navegadores Web, punto de contacto que casi todos tendremos con los pagos en línea. Además de los navegadores, y el ocasional cliente de correo, muchos otros servicios pueden emplear certificados de esta naturaleza para realizar autenticación o cifrado3 — Pero estos dos son los más visibles a los usuarios en general.

Bajo un esquema PKI, nuestro navegador confiará ciegamente en la identidad de un conjunto de CAs centrales, definidas por el proveedor del softare4. Mientras un certificado esté firmado por una autoridad conocida, el navegador mostrará la conexión como segura.

Tenemos por otro lado a los esquemas basados en el esquema de llaveros de confianza. Éste esquema fue dado a conocer en los 1990, con el sistema de criptografía PGP, de Phil Zimmermann. Un llavero de confianza podría definirse como un sistema colaborativo, par a par: Cada participante del llavero firma la llave de los otros participantes a los que conoce personalmente, certificando confianza en que su identidad es verdadera5. Cuando un usuario quiere comunicarse con otro, puede ver cuál es el camino de confianza yendo entre individuos, y en base a la distancia y grado de conexión (y, por tanto, de certificación) que tiene determianda identidad, decidir el nivel de confianza que depositará en ésta.

Entonces, un servidor seguro no es sólo el que implementa una conexión cifrada, sino que aquél en cuya identidad puedo confiar. Emplear cifrado sólo tiene sentido cuando podemos confiar en la identidad de nuestra contraparte. De muy poco serviría que garantizáramos que toda nuestra comunicación llega cifrada hasta nuestra contraparte si dicho sistema no es el sistema destino — Si no verificamos la identidad de nuestra contraparte, un atacante podría interponer un servidor entre nosotros y nuestro destino, descifrando y cifrando nuevamente la comunicación, modificando o guardando los datos que juzgara necesario.

En un esquema PKI, basta con engañar a una CA respecto a nuestra identidad para tener la puerta abierta a interceptar las solicitudes de usuarios. Y, tristemente, esto ya hace mucho tiempo pasó del terreno del discurso académico al del mundo real: En 2001 fue detectado un certificado firmado por Verisign a nombre de Microsoft, otorgado a un individuo sin relación alguna con dicha compañía6.

A diferencia de PKI, en que un conjunto de firmas se ve como una serie de árboles con raíces en cada una de las CAs certificadas, una red de firmas basada en las ideas de Zimmermann nos aparece como una red fuertemente interconectada, y nos permite validar varios caminos de confianza entre dos participantes de esta red, y evaluar cada a uno de ellos basado en la confianza subjetiva que damos a los actores involucrados7.

No hay un esquema indiscutiblemente mejor que el otro — Son utilizados con fines distintos. Ambos tienen su ámbito de aplicación — Y si hoy podemos confiar en la confidencialidad, integridad y seguridad de las transacciones en línea, es por estos esquemas. Nuevamente, de muy poco nos serviría cifrar nuestras transacciones en un entorno hostil sin tener confianza en que la contraparte es quien esperamos que sea.

Reputación del individuo

Asumamos, sin embargo, que la Sra. Abacha nos convenció plenamente de ser ella. ¿Debemos por ello confiar en su oferta?

Es aquí donde entra en juego la reputación: Ya que tengo certeza de estar interactuando con la entidad deseada, saber si es una entidad con la que me conviene mantener una transacción es el siguiente albur. Y, en este caso, la reputación es algo que debe establecerse bidireccionalmente. No sólo al comprador le interesa saber que el vendedor le entregará un producto genuino y a tiempo, sino que al proveedor le interesa saber si el comprador tiene cómo pagarlo. No sólo al solicitante de un préstamo le interesa que el banco confíe en su capacidad crediticia, sino que al banco le importa saber si éste no ha faltado a sus obligaciones de pago. Si entro a un sitio de intercambio entre particulares, sea de venta directa o a través de subastas (y seguramente en ambos casos todos habrán pensado en cuál sitio pensé al escribir tan amplia categoría — Eso también entra en el amplio ámbito de la reputación), los individuos participantes tienen una calificación indicando su confiabilidad basada en su comportamiento previo.

O, saliéndonos del árido tema de las transacciones económicas, en un foro de discusión puede interesarme filtrar los mensajes para sólo ver los que más vale la pena leer — Y, sin recurrir a un sistema que requiera involucramiento masivo de los editores, la mayor parte de estos sitios basan este filtro dando un valor inicial dependiente de la reputación del autor.

La asignación de reputación es un área completamente dependiente del campo de aplicación, por lo que resulta imposible hablar de implementaciones como en la sección anterior.

Nuevamente, las restricciones de espacio me dejan apenas arañando el campo, apuntando a un gran área a tener en consideración para cualquier desarrollo que emprendamos en que pueda involucrarse el peso o la complejidad de las relaciones entre entidades complejas. Tomar estos elementos en cuenta de forma transversal a los diferentes dominios de aplicación nos llevará a variadas e interesantes consideraciones, que seguramente mejorarán no sólo la confiabilidad de nuestras transacciones, sino incluso la oportunidad y el valor de la información que presentamos a nuestros usuarios.

Notas al pie

1 El nombre de la Sra. Abacha es el más prevalente en los fraudes de pago anticipado; tristemente, su identidad y reputación son ya demasiado bajos. Mi intención no es dañarlo más, claro está, sino señalar un fenómeno preexistente

2 Sin embargo, una pequeña proporción de una cantidad absurdamente grande de correos enviados sigue resultando en buen negocio… Y es por ello que estos defraudadores siguen saturando nuestros buzones.

3 Encontraremos referencias a estos certificados como X.509; si vamos a implementar directamente opeaciones sobre los certificados, conviene hacerlo empleando la biblioteca libre openssl.

4 Por ejemplo, puede consultar la lista de CAs autorizadas por Mozilla en http://www.mozilla.org/projects/security/certs/included/index.xml

5 Es muy importante tener en cuenta que lo único que aquí se certifica es la identidad, no la confianza en la entidad en cuestión. La confianza será tratada en la siguiente sección.

6 Bruce Schneier: Fake Microsoft certificates, http://www.schneier.com/crypto-gram-0104.html#7

7 Por poner un ejemplo, si yo (llave C1DB921F) obtengo un documento firmado por Marcelo Tosatti (llave E8E1FE55), desarrollador del kernel de Linux, encuentro que (al día en que escribo este texto) estamos a tres "brincos" de distancia: http://pgp.cs.uu.nl/paths/C1DB921F/to/E8E1FE55.html, http://webware.lysator.liu.se/jc/wotsap/wots/latest/paths/0xC1DB921F-0xE...

( categories: )

Los muchos significados del «cómputo en la nube»: Desmitificando el concepto

Submitted by gwolf on Fri, 01/14/2011 - 11:15
Wolf G.  2010.  Los muchos significados del «cómputo en la nube»: Desmitificando el concepto. Software Gurú. :48-49.

Uno de los términos ampliamente en boga en nuestro campo hoy en día es el cómputo en la nube. Tan en boga que me parece que se ha convertido en una palabra mágica, bastante hueca y sintomática de querer parecer en sintonía con los últimos desarrollos tecnológicos, sin comprenderlos en realidad. Además, al ser una frase que de golpe comenzó a escucharse con tanta insistencia, asumimos que es una estrategia nueva, una idea posibilitada por los nuevos avances tecnológicos — Y esto dista de ser el caso. En esta columna busco clarificar los conceptos y tipos básicos de cómputo en la nube, sus principales ventjas y desventajas, y brevemente encontrar paralelos con casos documentados de estos ”novedosos” conceptos

No critico, ojo, a las estrategias que están detrás de este sonoro término — Muy por el contrario, resultan muy convenientes a la hora de implementar, y como profesionales del desarrollo de software tenemos la obligación de estar familiarizados con ellas. Lo que critico es el abuso de un término ambíguo, que suele poner en problemas a los implementadores, al no estar claramente comprendido.

La mayor parte de las referencias que consulté mencionan a tres capas o niveles principales: El software –o la aplicación– como un servicio (Software as a Service, SaaS), la plataforma como un servicio (Platform as a Service, PaaS) y la infraestructura o el hardware como un servicio (Infrastructure as a Service, IaaS). Mi punto de vista, es que más que un nivel distinto, IaaS es un conjunto de cualidades adicionales que dan valor agregado a una oferta de PaaS.

SaaS: Bloques de construcción

Cuando uno de nuestros sistemas utiliza un API público remoto, o cuando para nuestro flujo de trabajo empleamos a una aplicación que reside y es administrada fuera de nuestra organización, estamos empleando SaaS. Nuestro proveedor puede cobrar su servicio de diversas maneras: A través de una renta directa, requiriendo del despliegue de publicidad, recibiendo autorización para recolectar nuestra información y hacer minería de datos sobre de ella — Hay una enorme variedad de esquemas, que refleja la gran variedad de niveles de integración que se puede dar a los servicios.

Notarán que la principal diferencia con los esquemas tradicionales cliente-servidor con que hemos trabajado desde hace décadas es el uso explícito de un proveedor externo — Sin embargo, técnicamente, nuestros sistemas no seguirán una lógica o un proceso de desarrollo diferente de como lo vienen haciendo por décadas.

Al mismo tiempo, sí hay una importante diferencia: Al no controlar nosotros el proceso que ocurre dentro de uno de nuestros subsistemas, nos vemos forzados a hacer bien lo que ya deberíamos estar haciendo: Implementar una separación limpia de responsabilidades entre los componentes, utiilzar exclusivamente las interfaces publicadas explícitamente — No brincarnos las capas de abstracción, como muchas veces estaríamos tentados a hacerlo en un proyecto meramente interno.

Hay un punto que no podemos perder de vista cuando empleamos un servicio de aplicaciones como parte de nuestra infraestructura en sistemas: Al depender de un tercero, ponemos en sus manos parte importante de nuestras promesas (y, por tanto, requerimientos), en tanto a disponibilidad, protección de datos y continuidad de la operación. Tocando el primer punto, resulta natural: Una falla en el servicio de cualquiera de nuestros proveedores de aplicaciones llevará a que nuestro sistema presente información incompleta o errónea a sus usuarios.

Respecto al segundo, no sólo debemos tener en cuenta las implicaciones directas (el proveedor tendrá acceso a todos los datos específicos que le enviemos para operar), sino que las indirectas: Todo ataque por medio del cual un agente hostil pueda llevar a cabo recolección de información sobre de nuestros proveedores resultará en la probable divulgación de información interna nuestra, e incluso el mismo proveedor puede estar realizando un seguimiento estadístico de nuestros patrones de uso, revelando mucho más de lo que podríamos querer darle.

En tanto a la continuidad de operación, si el proveedor del servicio decide cambiar los términos bajo los cuales brinda sus recursos, o si sencillamente deja de operar, podemos quedar sin una alternativa disponible para parte importante de nuestro flujo de trabajo.

Como corolario de la dependencia de terceros, al depender de aplicaciones como servicio perdemos la capacidad de planear las actualizaciones de nuestra infraestructura — Al depender de proveedores externos, en el momento en que cualquiera de sus APIs cambia, nos vemos forzados a actualizar de inmediato. Nuestros servicios en producción pueden fallar, y el mero concepto de rama estable pierde buena parte de su atractivo1.

PaaS+IaaS: Flexibilidad en el uso de recursos

Mientras que en el apartado anterior partíamos de que –desde el punto de vista del usuario– hay una aplicación central que corre en sus instalaciones y consume procesamiento realizado por componentes distribuídos por el mundo (”en la nube”), aquí la aplicación completa será desplegada en el proveedor de servicios. El usuario deja de emplear un servidor –o una granja de servidores– para consumir sólo los recursos. Y, consecuentemente, el esquema de utilidad para un proveedor de servicios bajo esta modalidad puede ir desde la renta a costo fijo hasta un cobro por uso de recursos, típicamente permitiendo reconfiguraciones dinámicas del paquete (del conjunto máximo de recursos) como respuesta a la demanda del sistema.

La justificación detrás de este concepto es que los servidores ”en casa” representan un gasto demasiado grande — Compra y actualización periódica de equipo, consumo eléctrico, uso de espacio físico y de ancho de banda, siendo que las instalaciones de la mayor parte de los usuarios no cuentan siquiera con un centro de datos. Además, todo servidor debe ser adquirido contemplando la carga estimada que sostendrá, pero contemplando un espacio para sobreconsumo en picos de actividad no planificable. En cambio, un proveedor masivo de servicios balancea el exceso de demanda de un usuario con la reducción de demanda de otro, permitiendo un menor sobredimensionamiento — Y una reducción neta de precios.

Nuevamente, apreciarán que la idea no es nueva — Los proveedores de presencia o de hosting existen desde hace muchos años en Internet. Más allá aún, precisamente este esquema fue planteado a mediados de los 1960, cuando fue desarrollado el sistema MULTICS2.

La principal diferencia con el esquema tradicional de renta de servidores es que bajo este esquema, el usuario deja de ser responsable de la administración incluso del servidor en el que se ejecutarán sus aplicaciones; el proveedor de servicios ofrece una cartera de plataformas administradas. Las plataformas pueden ser desde aplicaciones medianamente complejas, como plataformas Web que llevan un grado no trivial de configuración y representan una solución completa e integrada, hasta marcos para el desarrollo de sistemas (como Rails, Struts, Django, GWT), para los cuales los clientes sólo proveen el código específico de su aplicación, y aprovechan todo el andamiaje común que ya existe para una amplia base de clientes.

Respecto a consideraciones de seguridad y confiabilidad: Si bien al emplear PaaS no caemos ya en la trampa descrita en el primer apartado respecto a emplear código del cual no tenemos más conocimiento que el API, éste esquema sigue depositando todos nuestros datos en control del proveedor, en infraestructura compartida, con lo cual la probabilidad de que un ataque a un sistema sin relación aparente con el nuestro puede dar a un intruso acceso pleno a nuestra información.

El riesgo de que el proveedor cambie sus políticas de cobro a un esquema que no nos convenga se reduce también fuertemente, dado que en principio tenemos todo lo necesario para replicar la instalación con cualquier otro proveedor, o incluso migrar a una solución ”en casa”. Sin embargo, seguiremos sujetos a los días bandera de actualización forzada, dado que el proveedor típicamente ofrecerá la misma versión base a todos sus clientes — Y este factor puede dificultarnos una migración entre proveedores.

Hablando meramente de la conveniencia: Podemos enviar a uno de nuestros sistemas a la nube dados sus requisitos de almacenamiento de información, o de ancho de banda. Ahora bien, ¿cómo estamos realizando nuestros respaldos? Para poder hacer frente a contingencias, contar con una estrategia de respaldos buena y probada es mucho más importante aún que cuando hablamos de despliegues en infraestructura propia.

Por último, respecto al IaaS: Este resulta el más nebuloso y ambiguo — Ya no se trata de sistemas o entornos para que éstos se ejecuten, sino que de recursos de cómputo que están a nuestra disposición: Espacio en disco o en memoria, ”ticks” de procesador, ajuste de ancho de banda, como componentes discretos y ajustables en vivo — Atributos que se apoyan fuertemente en la virtualización para poder ser desplegados y controlados.

Hay quien incluye la virtualización de escritorios dentro de la definición de IaaS, pero si mucho, eso debe ser visto como consolidación de recursos y mejoría en el aprovechamiento de recursos, pero por calidad en el servicio debe hacerse utilizando recursos internos a la organización — Y por tanto, se aleja de las definiciones básicas de en la nube.

Llevamos años haciendo cómputo en la nube de un tipo o de otro. Al desmitificarlo un poco, espero abonar a una mejor utilización. Y al hablar acerca de los nada ignorables riesgos que conlleva, apunto a la cautela que debemos tener al adoptarlo, no entrar de lleno sólo porque es la moda.

  • 1. Podemos ver un buen ejemplo de esto cuando Twitter cambió su esquema de autenticación en agosto de este año, obligando a cientos de aplicaciones a impulsar la migración a nuevas versiones — http://blog.twitter.com/2010/08/twitter-applications-and-oauth.html
  • 2. El sistema operativo MULTICS partía del planteamiento de que el cómputo se convertiría en un servicio provisto centralmente, como la electricidad o el teléfono. http://www.multicians.org/
( categories: )

Entrevista en Canal 22: El movimiento del Software Libre

Submitted by gwolf on Sat, 10/23/2010 - 11:59
Written in...: 
2010

In February 2010, I was interviewed –along with other colleagues– for Canal 22's thematic Tuesdays. Here are both the edited program that was aired on March 2, 2010 and the full interview (on the same file as the full interview they held with Fernando Romo).

Full interview downloaded from Canal 22's space in Youtube

Resumen: 

En febrero de 2010, fui entrevistado –así como otros colegas– para los martes temáticos de Canal 22. Aquí están tanto el programa editado, que salió al aire el 2 de marzo del 2010, como la entrevista completa (en el mismo archivo que la entrevista completa que sostuvieron con Fernando Romo).

La entrevista completa fue descargada del Espacio de Canal 22 en Youtube.

Historia y futuro de la interfaz hombre-máquina

Submitted by gwolf on Mon, 08/23/2010 - 00:12
Wolf G.  2010.  Historia y futuro de la interfaz hombre-máquina. Software Gurú. :46-47.

Historia y futuro de la interfaz hombre-máquina

Frecuentemente nos mostramos maravillados de cómo ha avanzado la manera en que interactuamos con la computadora en los últimos años, sin detenernos a pensar cuánta verdad hay –o no– detrás de esta afirmación. A fin de cuentas, hace apenas unos años el uso de las computadoras era verdaderamente limitado, y hoy en día están en todos lados, y parecería que cualquiera es capaz de manejarlas — Al menos, en sus funciones básicas.

Yo sostengo que esta es una afirmación falsa, basada en las apreciaciones de gente ajena a nuestro campo, y producto más de la popularización que de un verdadero cambio cualitativo. Claro está, al poner en duda lo universalmente aceptado, recae en mí sustentar mi afirmación.

Vamos, pues, con un breve recorrido de cómo evolucionó la interacción hasta el día de hoy.

Los inicios

Las primeras décadas del desarrollo de la computación como una disciplina aplicada de la ingeniería (tomando como punto de partida de forma un tanto arbitraria a la primer computadora electrónica en 1943, ENIAC) verdaderamente vieron un cambio radical en la manera en que los usuarios se aproximaban a la computadora — Esto resulta poco más que una obviedad, dado que antes de 1943 no existían las computadoras siquiera.

Las primeras computadoras no tenían nada que hoy reconoceríamos como interfaz — Tanto instrucciones como datos eran introducidos directamente a las ubicaciones de memoria al iniciar la ejecución a través de tarjetas perforadas, y eran leídos de los registros del procesador, mostrándolos directamente en un volcado binario, hacia tarjetas o cintas perforadas, que debían ser traducidas a algo legible empleando dispositivos mecánicos independientes.

Teletipos y terminales

El primer avance resultó en una dirección obvia, pero facilitó tremendamente tanto el uso como el aprovechamiento de los resultados: La interfaz textual. No hablo aún de una pantalla, sino de la adecuación del teletipo, híbrido de teclado e impresora, que comenzó su existencia como un reemplazo más ágil y confiable que el código Morse para la comunicación a larga distancia. El teletipo permitía ingresar programas mucho más complejos a memoria, lo cual llevó a que naciera y popularizara un concepto que nos parece muy ajeno a las interfaces de usuario: El de los archivos. Aparecieron los primeros editores (obviamente, mucho más espartanos de lo que conocemos hoy, y orientados a trabajo línea por línea), y como consecuencia directa, los programas pudieron comenzar a presentar una mucho mayor complejidad – llevando a la introducción de bibliotecas de código y a las diversas abstracciones y estrategias para manejar la complejidad.

La transición del teletipo a la pantalla no es tan simple como podría parecer. Dejando de lado la mera complejidad técnica (relativa al estado del arte de la época) de crear dispositivos independientes y de relativo bajo costo capaces de mantener comunicación con la computadora central, generando la imagen en pantalla del texto que iban recibiendo — lo cual implicaba que tuvieran una memoria interna, aunque mínima para estándares modernos1, las ventajas de tener terminales con cierto grado de inteligencia se hicieron obvias, y comenzaron a aparecer terminales con diferentes estándares2 capaces de reposicionar el cursor, o de desplegar texto con atributos (negritas, subrayado, colores), caracteres semi-gráficos, hasta verdaderas capacidades de formularios como las que manejaban las IBM 3270, que comenzaron a permitir desacoplar la lógica de un programa de su presentación tal como hoy lo vemos en los navegadores Web.

Las terminales además fueron centrales para la aparición de computadoras multitarea/multiusuario. Quienes gustamos de utilizar sistemas Unix utilizamos como una de nuestras herramientas más poderosas al emulador de terminal, o como le dice el resto del mundo, la ventana negra. Si bien las terminales como producto de hardware hace mucho tiempo que ya no existen para propósitos prácticos, la interfaz de línea de comandos programable permite un grado de expresividad tan rico que no ha podido ser reemplazado por ningún otro medio.

WIMP: Window, Icon, Menu, Pointer

En diciembre de 1968, en los laboratorios de Palo Alto de Xerox, Douglas Engelbart presentó la que al día de hoy se conoce como la madre de todas las demos: La introducción de la interfaz gráfica básica que seguimos utilizando hoy en día, manejada a través de un apuntador controlado por un mouse, presentando ventanas para la visualización de las diferentes aplicaciones en uso (o vistas de una misma aplicación), iconos representando atajos a las acciones de sistema disponibles, y con un menú como elemento estándar presentando las acciones relacionadas con cada aplicación activa. Y, claro, para la entrada y manipulación de datos dentro de cada aplicación, el dispositivo primario seguirá siendo el teclado.

La demostración de Engelbart incluía ejemplos de aplicaciones verdaderamente revolucionarias en esa época, como la videoconferencia, el correo electrónico, el hipertexto o un editor colaborativo de tiempo real.

Y aunque ha habido refinamientos sucesivos y grandes cambios en la parte estética de esta propuesta, para las computadoras de uso general seguimos utilizando este esquema de interacción — Con más de cuarenta años de antigüedad.

Interfaces de propósito acotado

Posiblemente el mayor cambio en las interfaces de usuario viene de que, cada vez con mayor fuerza, tenemos dispositivos con gran poder de proceso de cómputo sin un formato de computadora de propósito general. No veo como casualidad que hoy veamos con entusiasmo a las interfaces tan revolucionarias como las presentes en teléfonos celulares o consolas de videojuego — Estamos llegando al punto en que vamos encontrando formas muy convenientes de interactuar con computadoras de propósito acotado, aunque éstas no sean adecuadas para aquellas de propósito general. Ilustro esto con dos ejemplos:

Con la generación actual de consolas de videojuegos, Nintendo se anotó el mayor éxito al introducir su Wii: Una consola de relativamente bajas prestaciones y muy inferior a su competencia en las áreas en que típicamente competían, la capacidad gráfica. Sin embargo, su apuesta más fuerte fue hacia una interfaz novedosa: Los controles basados en acelerómetros, que permitieron modelar diferentes actividades como nunca antes se habían presentado en videojuegos.

Por otro lado, el iPod de Apple introdujo una interfaz largamente prometida, basada en una pantalla táctil de tamaño reducido, y orientada a equipos destinados al entretenimiento, a la consulta rápida de información, y especialmente popularizada a través del teléfono aparecido poco tiempo después. Sin embargo, si bien esta interfaz ha resultado natural para una gran cantidad de personas, resultaría indudablemente impráctica y antiergonómica para una computadora de propósito general.

Y claro, seguiremos viendo nuevas interfaces dedicadas a tareas específicas — Las más exitosas serán, sin duda, las más transparentes: Las que se integren de manera transparente a las tareas, mejorando nuestra vida sin requerir que reparemos siquiera en su existencia. Por ejemplo, ¿cuánta gente está consciente de la cantidad de cálculos que se realizan en un automóvil? ¿No es acaso uno de los mejores ejemplos de cómputo ubicuo que se ha insertado silenciosamente en nuestras vidas?

Otras ideas

Han habido, claro, otras muchas propuestas de interfaces para uso general. En general no han sido bien aceptadas, o han sido empleadas sólo en contextos muy específicos. Algunos ejemplos son:

Pantallas táctiles
Desde mediados de los 1980, Hewlett-Packard introdujo su línea de computadoras HP110, con una pantalla sensible al tacto. Esta interfaz prometía ser más ágil y natural que el mouse (que requiere un nivel de coordinación no trivial). Y si bien esta interfaz tuvo un moderado éxito en áreas como los kioscos (cajeros automáticos, puntos de toma de pedido en restaurantes de comida rápida, etc.), nunca fue del todo aceptada para un uso general por lo poco ergonómico que resulta tener que levantar la mano constantemente para apuntar a puntos de la pantalla.
Reconocimiento de voz
La ciencia ficción de los 1970 (piensen en HAL de 2001 o en la serie Star Trek) presentó a la voz como la principal forma de interacción hacia la computadora (y un combinación de voz y despliegue en pantallas para los resultados). Se han ensayado interfaces de reconocimiento de voz, pero han fallado una tras otra por la dificultad que presenta el lenguaje humano, la alta variabilidad de los elementos que permiten reconocer las palabras en diferentes idiomas e incluso con diferentes acentos. Además de esto, fuera de dar comandos puntuales, "dictar" un texto a la computadora no es una tarea tan simple como podría parecer: Al redactar un texto, el proceso normal que seguimos implica ir hacia atrás y hacia adelante, corrigiendo el texto, reemplazando y reformulando las frases. Una interfaz de dictado deberá saber discriminar el texto de las órdenes, lo cual requerirá un entrenamiento nada trivial al usuario.
Manipulación 3D
Presentar la información como objetos del mundo real, manipulables a través de guantes o gestos, aparece como muy atractivo. Podemos ver un ejemplo muy cuidadosamente desarrollado de una interfaz basada en estas ideas en la película Minority Report (Steven Spielberg, 2002). El poder de procesamiento y el hardware especializado para hacer este tipo de manipulaciones, sin embargo, no justifica –al día de hoy– el costo que significaría. Hay aplicaciones, claro, para las que este costo sí se justifica; en México, la Dirección General de Servicios de Cómputo Académico de la UNAM cuenta con la computadora especializada Ixtli para simulación y visualización, y si están interesados en conocer esta tecnología pueden solicitar visitas guiadas demostrando su funcionamiento.

Claro, hay muchas más ideas en el tintero, y en los años por venir seguro veremos varias más. Sin embargo, no ha habido cambios substantivos en las interfaces para interactuar con una computadora de propósito general en los últimos 40 años. Me resisto a creeer que esto sea porque el modelo actual sea perfecto; como desarrolladores, tenemos la tarea de proponer, adoptar y evaluar nuevos modelos de interacción.

1 Una terminal tonta, la categoría más simple de terminal, requiere de una memoria interna a partir de unos 2KB en anillo para representar los 80x25 caracteres que puede presentar en pantalla; (no olvidemos que las primeras computadoras personales que se vendieron al público, ya en los 1970, tenían típicamente una memoria total de 4Kb)

2 Estándares como ANSI, VT100, VT220, Wyse, y muchos otros que aún a 40 años de su introducción siguen en amplio uso

( categories: )

Prácticas seguras de programación para sistemas Web

Submitted by gwolf on Mon, 08/09/2010 - 23:36
Wolf G.  2009.  Prácticas seguras de programación para sistemas Web. Ventana informática. :91-111.

La Evolución del rol que cumplen los sistemas en las organizaciones ha cambiado por completo -afortunadamente- el punto de vista que la mayor parte de los desarrolladores tiene con respecto a la seguridad.

Hace una o dos décadas, el tema de la seguridad en cómputo era frecuentemente evitado. Y hasta cierto punto, esto era muy justificable: ¿Intrusos? ¿Integridad? ¿Validaciones? Conceptos que hoy a todos parecen fundamentales eran vistos como distracciones teóricas que sólo entorpecían la usabilidad de los sistemas. En la década de los 80 había muy poco software diseñado para su operación en red, y mucho menos para la idea de red que tenemos hoy en día. Y si bien es cierto que la mayor parte de los ataques se origina -y siempre se ha originado- dentro del perímetro de confianza de nuestra organización, hace 20 años sencillamente había menos información sensible alojada en medios electrónicos, menos gente con el conocimiento necesario para manipularla, e incluso la manipulación tenía efectos más nocivos: Si bien hace años la infraestructura de cómputo era el soporte facilitador, la copia maestra estaba en papel - Hoy en día estamos transitando hacia la situación opuesta, en que la versión electrónica es la primaria. Hoy, una intrusión en nuestros sistemas puede poner en jaque la integridad de la información primaria.

Mantener la seguridad en los sistemas que desarrollamos implica un alto costo: Los programadores tienen que aprender a evitar errores comunes; tenemos que concientizarnos y acostumbrarnos a dedicar recursos a implementar baterías de pruebas; tienen que entrar en juego validaciones y conversiones sobre los datos que manipulamos, con costos en tiempos de procesamiento de cada solicitud... Pero, afortunadamente, ha crecido también la conciencia de la importancia de destinar a la seguridad la atención y recursos que requiere.

El problema sigue siendo, coloquialmente... /¿con qué se come?/ La seguridad en cómputo sigue siendo un campo dificil de entender, con muchas aristas ocultas. Es por esto que en este artículo abordaremos algunos temas fundamentales, que a pesar de ser bien conocidos, siguen siendo origen de un sinfín de nuevos sistemas creados con obvios agujeros.

1. ¿Qué es la seguridad en cómputo?
--------------------------------

A riesgo de que parezca perogrullada, un /sistema seguro/ no es otra cosa que un sistema /que responde como debe/, un sistema que /cubre las necesidades y requerimientos con que fue concebido/. Claro, a esta pregunta hay que verla a la luz de varios criterios para que en verdad signifique algo. Intentemos nuevamente. Un sistema seguro presenta:

Consistencia: Ante las mismas circunstancias, debe presentar el mismo comportamiento. Ante un sistema /seguro/, el tradicional remedio "¿ya intentaste reniciarlo?" no surte efecto. Si una grandísima proporción de usuarios se ha acostumbrado a que un reinicio resuelve las cosas, no es sino porque el ambiente de ejecución se ensucia con elementos que debería haber descartado - Y por tanto, podemos concluir que sistemas en cuestión son inherentemente inseguros.

Protección y separación: Los datos, instrucciones y espacio de memoria de un programa, componente o usuario no deben interferir ni permitir interferencia de otros. Las condiciones anormales ocurridas en uno de los componentes -sean accidentales o expresas- deben tener un impacto mínimo en el sistema como un conjunto.

Autenticación: El sistema debe poseer los mecanismos necesarios para asegurarse que un usuario es realmente quien dice ser.

Control de acceso: Nuestro sistema debe poder controlar con toda la granularidad necesaria los permisos de acceso a sus datos - Quién tiene acceso a qué recursos, y qué tipo de acceso tiene.

Auditoría: El sistema debe ser capaz de registrar, así como de notificar a quien sea necesario, de cualquier anomalía o evento importante.

Claro está, todos estos atributos deben ir matizados, priorizándolos al nivel /adecuado/ a nuestras necesidades. Ir demasiado lejos en uno de estos objetivos puede ser de hecho perjudicial para los fines de nuestro sistema - Por poner un ejemplo, es de todos bien conocido que el tradicional esquema de autenticación basado en usuario y contraseña es fácil de engañar; basta adivinar (o conseguir) un pedazo de información, típicamente de muy débil complejidad, para estar autenticado como determinado usuario. En México, desde hace algunos años, los bancos exigen la identificación del cliente a través de dispositivos que presenten una mucho mayor complejidad, generando cadenas de números que cambian periódicamente. Pero, obviamente, poca gente requerirá un nivel de seguridad similar a éste, o basado en parámetros biométricos, para abrir su cuenta de correo. Si bien es aceptable demandar que un usuario bancario tenga acceso al dispositivo como una medida de seguridad (lo cual es una inconveniencia, dado que muchos usuarios prefieren no cargarlo consigo constantemente - Precisamente pensando en la seguridad) es aceptable, requerir medidas similares para acceso al correo electrónico sería de inmediato criticado por todos los usuarios como excesivo y como limitante a la usabilidad.

Y otra anotación: Nos es natural aspirar a la perfección, al 100%. Sin embargo, dice el refrán que "lo perfecto es enemigo de lo bueno". Es importante que, en toda etapa de la planeación, desarrollo, implantación y tiempo de vida de un sistema recordemos que un 100% de seguridad es una utopía, un objetivo que sólo puede servir para guiar nuestro trabajo diario.

Los programas son escritos por humanos, y son también humanos quienes administran los sistemas en que corren. Hay una gran cantidad de interacciones entre los elementos de un programa y el sistema, y un cambio en cualquiera de ellos puede tener consecuencias inesperadas si no se hace con cuidado y conciencia. Constantemente aparecen nuevas categorías de errores capaces de llevar a problemas de seguridad. Parte fundamental de nuestra actuación como profesionales en nuestro campo debe ser el mantenernos al día con los últimos desarrollos y las últimas amenazas.

2. El estado actual de la seguridad en cómputo
-------------------------------------------

En un estudio publicado a inicios del 2009, SANS y MITRE (SANS/MITRE, 2009) publicaron la lista de los 25 errores de seguridad más importantes en frecuencia y relevancia. Este listado incluye muchos temas fundamentales, que deberían ser comprendidos por cualquier programador que se diga profesional. Vienen explicados con un buen nivel de detalle, detallando cómo evitar o mitigar sus efectos. Éste estudio ha contado con amplio apoyo por parte de diversos sectores tanto de la academia como de la industria.

Sin embargo, volviendo al punto con el que inicia el presente texto: En realidad, ¿el hecho de que ahora la seguridad en cómputo sea un tema que forma parte del inconsicente colectivo /realmente/ ha llevado al mejoramiento de la calidad general del código? Es muy posible que estemos ante un claro caso de falso sentido de seguridad.

Usuarios y programadores están al tanto del peligro que corren al utilizar un sistema informático. Obviamente, los programadores están (o por lo menos, deberían estar) más atentos, dado que se dedican profesionalmente al cómputo y que comprenden (o, nuevamente, deberían comprender) mucho mejor las interacciones que llevan a las principales vulnerabilidades. Sin embargo, una y otra vez nos topamos con sistemas con las mismas clases básicas de errores, de tremendos agujeros de seguridad a través de los cuales podrían pasar marchando regimientos completos.

¿Por qué sucede esto? En síntesis, podría resumirse en los siguientes factores:

• Confianza ciega en la propaganda de los proveedores. Prácticamente todos los proveedores de herramientas de desarrollo, de modelado, de verificación aseguran ser «la más segura». En el mundo del software propietario (en contraposición al Software Libre), muchos usuarios tienden a creer en la voz de los "gurúes" sin poderla verificar, dado que no pueden siquiera validar lo que éstos anuncian.

Si bien algunos modelos de programación de hoy en día /sí/ nos protegen de las más graves fallas técnicas, muchos programadores se sentirán mágicamente protegidos por la tecnología que tenga la mezcla correcta de palabras de moda, sin verdaderamente averiguar qué es lo que dichas palabras significan. No hay peor ceguera que la de quien no quiere ver, dicen — Y no hay peor profesional que el que cree en un acrónimo sin comprender su significado.

• Falta de actualización. Al estudiar acerca de los peligros relativos a la seguridad, seguimos pensando en las mayores amenazas de seguridad que fueron estudiadas y descubiertas hace décadas — Desbordamiento de pila (Wheeler, 2003), desbordamiento de enteros (Williams, 2009)... Sin embargo, pocos cursos relativos a la seguridad se han actualizado a los errores que plagan a la clase actual de aplicaciones, mayormente desarrolladas en marcos que inherentemente se protegen de estos graves errores de hace décadas.

• El «Síndrome NIH» (Not Invented Here - No Inventado Aquí (Wikipedia, 2008-2009a)) es un antipatrón de diseño definido como la tendencia a reinventar algo ya existente, basándose en la casi siempre falsa creencia de que un desarrollo llevado a cabo "en casa" resultará más adecuado, más seguro o más controlado que implementaciones ya existentes.

• Procedimientos tediosos. Muchos errores de diseño de sistemas podrían ser fácilmente paliados si los desarrolladores no tuvieran que llevar a cabo prácticas engorrosas o tediosas para evitarlos. Por poner un ejemplo, casi la totalidad de los lenguajes desarrollados en las últimas décadas liberan a sus desarrolladores del manejo (asignación, liberación) explícito de bloques de memoria. Si bien esto reduce sensiblemente la rapidez del software desarrollado y resta un poco de flexibilidad (imposibilita trabajar sobre conjuntos de datos con simple aritmética de apuntadores), evita por completo los principales problemas que plagaban al desarrollo de software en los años 80 y 90 — Como es mencionado dos párrafos atrás, los diversos tipos de desbordamiento, o errores típicamente no asociados con la seguridad, como las dobles liberaciones (MITRE, 2009-2009a) o las fugas de memoria (Erickson, 2002).

Es por esto que resulta tan importante enfatizar en explicar y enseñar respecto a las principales vulnerabilidades de que al día de hoy hay que cuidarse.

Una grandísima proporción de los sistemas desarrollados hoy en día, siguen el paradigma cliente-servidor. Y si bien hay muy diferentes maneras de implementar sistemas cliente-servidor, indudablemente la más difundida y prevalente hoy por hoy es la de los sistemas Web. Al mismo tiempo, dado medio mismo a través del cual se transporta y por el ámbito global que han adquirido muchos de éstos sistemas, que encontraremos bajo éste modelo de desarrollo la categoría más expuesta directamente a elementos hostiles — Esta es la razón por la que el presente artículo adopta un enfoque principalmente orientado a éste tipo de sistemas.

Si bien hay una tremenda cantidad de categorías en las cuales podríamos ahondar, juzgamos que los tres casos presentados a continuación son una buena muestra de las vulnerabilidades más comunes, peligrosas, y aquellas de las cuales es más importante estar consciente y atento al programar — O al auditar sistemas existentes.

3. Ejemplo 1: Inyecciones de SQL
-----------------------------

Las vulnerabilidades más comunes son también las más fáciles de explotar para un atacante - Y utilizando algunas prácticas base, son también las más fáciles de evitar o corregir: Casi todas ellas se originan en la falta de validación (o exceso de confianza) en los datos proporcionados por el usuario.

Prácticamente la totalidad de los sistemas desarrollados procesarán datos provenientes de terceros. Ya sea mostrando o grabando lo expresado en formas HTML, determinando el flujo de la aplicación a través de rutas y parámetros o «galletas» HTTP (ver sección 4), o incluso -considerando la tendencia de migración hacia un esquema de «cloud computing»- tomando resultados de procedimientos remotos en sistemas no controlados por nosotros, a cada paso es necesario emplear datos no confiables, o generados por una entidad no confiable.

Esta puerta de entrada permite a un atacante una amplia variedad de modalidades de intrusión. En general, podemos hablar de ellas como inyección de código interpretado - Dedicaremos nuestra atención a la inyección de SQL (Wikipedia, 2004-2009b).

En el desarrollo de sistemas debemos partir siempre del principio de mínima confianza: No debemos confiar en ningún dato proveniente de fuera de nuestro sistema, independientemente de quién sea el usuario. Esto es especialmente importante cuando requerimos que un elemento cruce entre las principales barreras de las diversas capas de nuestro sistema.

3.1 Comprendiendo la inyección
--------------------------

Tomaremos como ejemplo un URL típico generado por uno de los sistemas de administración de contenido (CMS) en boga hoy en día: Joomla. Por razones obvias, el nombre verdadero del sitio en cuestión ha sido reemplazado por «www.ejemplo.com».

http://www.ejemplo.com/content/view/825

Todos hemos analizado URLs, y resultará obvio que «825» corresponda al ID de la nota en la base de datos, y que los componentes «content» y «view» indiquen la operación que el sistema debe realizar ante una solicitud. Ahora bien, ¿a qué me refiero a que cruzamos las barreras entre las capas? ¿Y cuáles son las principales?

Enfoquémonos en el ID. Al analizar el URL, el ID es un pedazo de texto (formalmente es una cadena que es recibida como parte del método GET, uno de los métodos definidos para el protocolo HTTP). El servidor Web que recibe mi solicitud interpreta este método GET y encuentra -utilizando mod_rewrite, en caso de tratarse de un servidor Apache como la mayoría de los sitios de la red, a través de configuración típicamente indicada en el archivo .htaccess- que el contenido indicado por la ruta «/content/view/*» debe ser procesado por el archivo index.php, que a su vez (dada su terminación o demás reglas que pueden aplicarse) es manejado por el lenguaje PHP. El archivo index.php es provisto por el sistema Joomla, que reconoce la ruta, convierte al ID en su representación numérica y lo utiliza para pedir a la base de datos le entregue los datos relacionados con determinado artículo. Entonces, aquí podemos reconocer los siguientes puntos principales de manipulación de la solicitud:

• Apache recibe una solicitud HTTP, y (via mod_rewrite) la reescribe, indicando «content», «view» y «825» como parámetros a index.php
• PHP analiza, separa y estructura los parámetros recibidos para ser utilizados por Joomla
• Joomla solicita el artículo 825 a la base de datos

La variabilidad de los primeros pasos es en realidad menor - Pero al solicitar a la base de datos el artículo «825» (y este es el caso base, el más sencillo de todos) deben pasar muchas cosas. Primero que nada, «825» es una cadena de caracteres. PHP es un lenguaje débilmente tipificado (los números se convierten en cadenas y viceversa automáticamente según sea requerido), pero una base de datos maneja tipos estrictamente.

Un atacante, una persona que quiere causar daño u obtener acceso mayor al cual tiene autorizado en nuestro sistema, basará su acercamiento en ser creativo respecto a cómo engañar a nuestro sistema: Es muy poco frecuente que busque adivinar usuarios/contraseñas; más bien, intentará engañar al sistema para entregar resultados distintos de aquello que parece estar siendo solicitado. ¿Y cómo se engaña a un sistema? Pidiéndole algo que no se espere - Por ejemplo, «825aaa». En este caso (¡felicidades!), el código PHP que invoca a la base de datos sí verifica que el tipo de datos sea correcto: Hace una conversión a entero, y descarta lo que sobra. Sin embargo, en muchos sistemas desarrollados a en casa, una solicitud similar lleva a un mensaje como el siguiente:

Warning: pg_execute() [function.pg-execute]: Query failed: ERROR: invalid input syntax for integer: "825aaa" in /home/(...)/index.php on line 192

Esto indica que uno de los parámetros fue pasado sin verificación de PHP al motor de base de datos, y fue éste el que reconoció al error.

Ahora, esto no califica aún como inyección de SQL (dado que el motor de bases de datos supo reaccionar ante esta situación), pero estamos prácticamente a las puertas. El código desarrollado por una determinada persona en un lapso de tiempo dado tiende a repetir muchos patrones — Es casi un hecho que si desarrollador no validó la entrada en un punto, habrá muchos otros en que no lo haya hecho. Este error en particular nos indica que el código construye la cadena de consulta SQL a través de una interpolación parecida a la siguiente:

$id_art = $_GET['id'];
$sql = "SELECT * FROM articulo WHERE id = $id_art"

La vulnerabilidad aquí consiste en que el programador no tomó en cuenta que $id_art puede contener cualquier cosa enviada por el usuario - Por el atacante en potencia. ¿Cómo puede aprovecharse esto? No hay límites más que la imaginación.

Presentamos a continuación algunos ejemplos, evitando enfocarnos a ningún lenguaje en específico - Lo importante es el proceso y el tratamiento que se da al SQL generado.

Para estos ejemplos, cambiemos un poco el caso de uso — Nuevamente, recordando que errores derivados del estilo de codificación detectados en un punto muy probablemente se repitan a lo largo del programa. En vez de ubicar recursos, hablemos acerca de una de las operaciones más comunes: La identificación de un usuario vía login y contraseña. Supongamos que el mismo sistema del código recién mencionado utiliza la siguiente función para validar a sus usuarios:

$data = $db->fetch("SELECT id FROM usuarios WHERE login = '$login' AND passwd = '$passwd'");
if ($data) {
$uid = $data[0];
} else {
print "<h1>Usuario inválido!</h1>";
}

Aquí pueden apreciar la práctica muy cómoda y común de /interpolar/ variables dentro de una cadena - Muchos lenguajes permiten construir cadenas donde se expande el contenido de determinadas variables. En caso de que su lenguaje favorito no maneje esta característica, concatenar las sub-cadenas y las variables nos lleva al mismo efecto. Por ejemplo, en VisualBasic obtendríamos la misma vulnerabilidad construyendo nuestra cadena así:

Dim sql as String = "SELECT id FROM usuarios WHERE login = '" & login & "' AND passwd = '" & passwd & "'"

Sin embargo... ¿Qué pasaría aquí si el usuario jugara un pequeño truco? Si solicitara, por ejemplo, entrar al sistema utilizando como login a «fulano';--», esto llevaría al sistema a ignorar lo que nos diera por contraseña: Estaríamos ejecutando la siguiente solicitud:

SELECT id FROM usuarios WHERE login = 'fulano';--' AND PASSWD = ''

La clave de este ataque es confundir a la base de datos para aceptar comandos generados por el usuario - El ataque completo se limita a cuatro caracteres: «';--». Al cerrar la comilla e indicar (con el punto y coma) que termina el comando, la base de datos entiende que la solicitud se da por terminada, y cualquier cosa que siga es otro comando. Podríamos enviarle más de un comando consecutivo que concluyera de forma coherente, pero lo más sencillo es utilizar el doble guión indicando que inicia un comentario. De este modo, logramos vulnerar la seguridad del sistema, entrando como un usuario cuyo login conocemos, aún desconociendo su contraseña.

Pero podemos ir más allá - Siguiendo con este ejemplo, típicamente el ID del administrador de un sistema es el más bajo. Imaginen el resultado de los siguientes nombres de usuario falsos:

ninguno' OR id = 1;--

«ninguno» no es un usuario válido del sistema, pero ésto resultaría en una sesión con privilegios de administrador.

'; INSERT INTO usuarios (login, passwd) VALUES ('fulano', 'de tal'); --

Esto no otorgaría en un primer momento una sesión válida, pero crearía una nueva cuenta con los valores especificados. Obviamente, es posible que fuera necesario averiguar -a prueba y error- qué otros valores es necesario agregar para que ésto nos otorgue un usuario válido con suficientes privilegios.

'; DROP TABLE usuarios; --

Un atacante puede darse por satisfecho con destruir nuestra información. En este caso, el atacante destruiría de un plumazo nuestra tabla de usuarios. Me es imposible dejar de sugerirles visitar al ya famoso «Bobby Tables» (Munroe, 2007).

3.2 Evitando las inyecciones de SQL
-------------------------------

¿Y qué podemos hacer? Protegerse de inyección de SQL es sencillo, pero hay que hacerlo en prácticamente todas nuestras consultas, y convertir nuestra manera natural de escribir código en una segura.

La regla de oro es nunca cruzar fronteras incorporando datos no confiables - Y esto no sólo es muy sencillo, sino que muchas veces (específicamente cuando iteramos sobre un conjunto de valores, efectuando la misma consulta para cada uno de ellos) hará los tiempos de respuesta de nuestro sistema sensiblemente mejores. La respuesta es separar /preparación/ y /ejecución/ de las consultas. Al preparar una consulta, nuestro motor de bases de datos la compila y prepara las estructuras necesarias para recibir los parámetros a través de «placeholders», marcadores que serán substituídos por los valores que indiquemos en una solicitud posterior. Volvamos al ejemplo del login/contraseña. En este caso, presentamos el ejemplo como sería construído desde el lenguaje Perl:

$query = $db->prepare('SELECT id FROM usuarios WHERE login = ? AND passwd = ?');
$data = $query->execute($login, $passwd);

Los símbolos de interrogación son enviados como literales a nuestra base de datos, que sabe ya qué le pediremos y prepara los índices para respondernos. Podemos enviar contenido arbitrario como login y password, ya sin preocuparnos de si el motor lo intentará interpretar.

Algunos lenguajes y bibliotecas de acceso a bases de datos (notablemente, la popular combinación PHP+MySQL) no implementan la funcionalidad necesaria para separar los pasos de preparación y ejecución. La respuesta en dichos lenguajes es utilizar funciones que /escapen/ explícitamente los caracteres que puedan ser nocivos. En el caso mencionado de PHP+MySQL, esta función sería mysql_real_escape_string (Achour, 1997-2009):

$login = mysql_real_escape_string($_GET['login']);
$passwd = mysql_real_escape_string($_GET['passwd']);
$query = "SELECT id FROM usuarios WHERE login = '$login' AND passwd = ''";
$data = $db->fetch($query);

Una gran desventaja que ésto conlleva es la cantidad de pasos que el programador debe efectuar manualmente — Especialmente cuando entran en juego mayores cantidades de datos, y hay que verificar a cada uno de ellos. Cabe mencionar que las bibliotecas que implementan conectividad a otras bases de datos para PHP (por ejemplo, PostgreSQL) sí ofrecen las facilidades necearias para la preparación y ejecución como dos pasos separados, con una semántica muy similar a la anteriormente descrita.

Revisar todas las cadenas que enviamos a nuestra base de datos puede parecer una tarea tediosa, pero ante la facilidad de encontrar y explotar este tipo de vulnerabilidades, bien vale la pena. En las referencias (Mavituna, 2007), (Microsoft, 2009), (Friedl, 2007) podrán encontrar información mucho más completa y detallada acerca de la anatomía de las inyecciones SQL, y diversas maneras de explotarlas aún incluso cuando existe cierto grado de validación.

3.3 Alternativa específica: El uso de ORMs
--------------------------------------

Otra alternativa interesante que todo programador debe conocer es el uso de marcos de abstracción, como los mapeos objeto-relacionales (ORMs (Wikipedia, 2007-2009c)). Estos marcos se encargan de crear todo el "pegamento" necesario para hermanar a dos mundos diferentes (el de las bases de datos relacionales, basados en registros almacenados en tablas, y el de la programación orientada a objetos, basado en objetos instanciados de clases), mundos que comparten algunas características (estructura homogénea a todos los elementos del mismo tipo) pero no otras (manejo de relaciones complejas como composición o agregación). Pero, en el ámbito aquí discutido, la principal característica de los ORMs es que se encargan de cubrir los detalles relativos a la integración de dos lenguajes y dos formas de ver al mundo muy distintas. Uno de los grandes atractivos de los ORMs es que, entre otras muchas ventajas, su uso nos puede liberar por completo (o en una gran medida) de escribir directamente código SQL en la totalidad de nuestro proyecto.

Una solicitud similar a la anterior a través del ORM ActiveRecord en el lenguaje Ruby sería sencillamente:

usuario = Usuario.find_by_login(login, :conditions => 'passwd = ?', passwd)

En este caso, sería directamente la biblioteca la que convierte una llamada find a la clase Usuario (que hereda de ActiveRecord::Base) en una consulta SQL, escapa/limpia las entradas y envía la solicitud a la base de datos. Esto proporciona la ventaja adicional de que todo el código relativo al paso de parámetros a la base de datos está concentrado en un sólo punto (y en un punto ampliamente utilizado y escrutado por profesionales de todo el mundo); toda omisión o error que vaya siendo detectado llevará a que éste sea corregido en un sólo punto — Y nuestra aplicación recibirá, en todas las consultas que pasen a través del ORM, el beneficio de esta mejoría. Además, estando desarrollada verdaderamente en un sólo lenguaje, la aplicación se mantiene más limpia y resulta más fácil de comprender y depurar.

ActiveRecord es sólo uno de muchos ORMs existentes; está implementado para Ruby (Heinemeier, 2004-2009) y para .NET (Verissimo, 2003-2009), y hay implementaciones muy cercanas a este patrón en varios otros lenguajes.

4. Ejemplo 2: Cross-Site Scripting (XSS)
-------------------------------------

Como ya mencionamos, las inyecciones se presentan cuando /cruzamos fronteras/. Las inyecciones SQL no son, ni mucho menos, el único tipo de inyección de código malicioso; cualquier lugar en que un mismo dato puede significar cosas diferentes dependiendo del contexto en que es evaluado es susceptible a ser vulnerable a inyección. Las siguientes categorías las menciono únicamente para invitar al lector a empaparse en la problemática que conllevan.

Del mismo modo que debemos /sanitizar/ (limpiar, escapar) los datos que llevamos hacia abajo a través de las capas del diseño de nuestro sistema, debemos hacer lo mismo al subir. ¿Qué significa esto? Que no sólo debemos proteger las capas privadas de nuestra aplicación, a las cuales un posible atacante no debería tener acceso directo, sino que debemos también proteger a las capas superiores — Aquellas que están incluso fuera de nuestro control — Como el navegador de nuestros demás usuarios (MITRE, 2008-2009b).

En un sistema Web (especialmente si queremos participar de eso a lo que llaman /Web 2.0/, muchas veces desplegaremos a un usuario información provista por otros usuarios. Para ofrecer toda la funcionalidad y respuesta ágil de un sitio moderno, los navegadores están típicamente configurados para ejecutar todo código JavaScript que reciben, confiando en quien lo origina. Ahora, ¿qué pasaría si un usuario malicioso deja en un blog el siguiente comentario?

window.location="http://www.hackme.org/1234";

En efecto, inmediatamente al cargar la página en cuestión, el usuario sería redirigido a un sitio diferente (y con justa razón: Para su navegador, ésta indicación viene del sitio, no puede saber que viene de un elemento hostil).

Éste sitio podría hacerse pasar por el sitio víctima, sin que el usuario se diera cuenta, y podría llevar al usuario a diferentes escenarios donde se le extrajera información confidencial - Por ejemplo, se le puede pedir que se vuelva a autenticar. La mayor parte de los usuarios caerán en el engaño, con lo que éste puede volverse un potente mecanismo para suplantación de identidad.

Por si no bastara, cada usuario, obviamente, tiene un perfil diferente — y normalmente, nivel o credenciales de acceso también diferentes. ¿Qué pasaría si el código JavaScript fuera muy ligeramente más malicioso? Por ejemplo (y nuevamente, meramente como ejemplo ficticio), podría hacer que un administrador le diera acceso completo al sitio. Si el atacante recibió el usuario número 152, podría enviar un mensaje privado al administrador del sitio que incluyera:

window.location="/admin/user/152/edit?set_admin=1";

Claro está, asignar una nueva dirección a window.location es probablemente la manera más burda y notoria para llevar a cabo estos ataques; hay muchas maneras más sigilosas, que pueden pasar completamente desapercibidas por un usuario casual.

Este tipo de ataques son conocidos genéricamente bajo el nombre de /XSS/ o /Cross-Site Scripting/ (MITRE, 2008-2009c). La clave para evitarlos es nuevamente sanitizar toda la información — Pero en este caso, toda la información a enviar al cliente. En el caso de HTML, prácticamente basta con escapar ciertas /entidades/ (caracteres que pueden tener significados especiales). Por ejemplo, reemplazando todos los caracteres «<» por su representación «<» y todos los caracteres
«>» por «>» (como primer acercamiento), éste código será desplegado de una manera limpia. Éste es típicamente un proceso aún más engorroso que sanitizar la entrada, por la cantidad de puntos donde hay que repetir la validación. Dependiendo del caso, muchos desarrolladores optan por limpiar a la entrada todo lo que será eventualmente desplegado — pero esto puede llevar al usuario a algunas condiciones en que el navegador no lo /des-sanitiza/, resultando sencillamente en una salida aparentemente llena de basura.

Como nota adicional: Es posible (y altamente deseable) sanitizar toda la información a la entrada, desechando todo lo que no sea claramente aceptable. Sin embargo, siempre hay casos en que requerimos guardar la información completa y sin manipular.

Si bien evitar XSS es más dificil que evitar inyecciones de SQL. A pesar de que el impacto de XSS, a primera vista, es menos severo que el de una inyección de SQL, éste puede tardar mucho más tiempo en ser corregido, puede estar presente en más puntos de nuestro código, y por sobre todo, es más tedioso de arreglar — Por lo que es fundamental acostumbrarnos a verificar toda la información que despleguemos /a tiempo/.

Además, si bien el impacto es menos inmediato, es típicamente más sigiloso. Si un atacante obtiene la información de acceso de una gran cantidad de usuarios, para propósitos prácticos no podemos volver a confiar en ninguno de nuestros usuarios — Todos pueden estar potencialmente controlados por el atacante.

5. Ejemplo 3: Manejo de sesiones a través de galletas HTTP
-------------------------------------------------------

La conjunción de un protocolo verdaderamente simple para la distribución de contenido (HTTP) con un esquema de marcado suficientemente simple pero suficientemente rico para presentar una interfaz de usuario con la mayor parte de las funciones requeridas por los usuarios (HTML) crearon el entorno ideal para el despliegue de aplicaciones distribuídas.

Desde sus principios, el estándar de HTTP menciona cuatro /verbos/ por medio de los cuales se puede acceder a la información: GET (solicitud de información sin requerir cambio de estado), POST (interacción por medio de la cual el cliente manda información compleja y que determinará la naturaleza de la respuesta, así como posibles cambios de estado del lado del servidor), PUT (creación de un nuevo objeto en el servidor) y DELETE (destrucción de un determinado objeto en el servidor). Sin embargo, por muchos años, los verbos fueron mayormente ignorados — La mayor parte de los sistemas hace caso omiso a través de qué verbo llegó una solicitud determinada; muchos navegadores no implementan siquiera PUT y DELETE, dado su bajísimo nivel de uso — Aunque con la popularización del paradigma REST (Costello, s.f.), principalmente orientado a servicios Web (interfaces expuestas vía HTTP, pero orientadas a ser consumidas/empleadas por otros programas, no por humanos), esto probablemente esté por cambiar.

El protocolo HTTP, sin embargo, junto con su gran simplicidad aportó un gran peligro — No una vulnerabilidad inherente a los sistemas Web, sino que un peligro derivado de que muchos programadores no presten atención a un aspecto fundamental de los sistemas Web: Cómo manejar la interacción repetida sobre de un protocolo que delega el mantener el estado o /sesión/ a una capa superior. Esto es, para un servidor HTTP, toda solicitud es única. En especial, un criterio de diseño debe ser que toda solicitud GET sea /idempotente/ — Esto significa que un GET no debe alterar de manera significativa el estado de los datos — Es aceptable que a través de un GET, por ejemplo, aumente el contador de visitas, (que haya un cambio no substantivo) — Pero muchos desarrolladores han sufrido por enlazar a través de un GET (como todo navegador responde a una liga HTML estándar), por ejemplo, el botón para eliminar cierto objeto.

¿Cuál es el peligro? Que diversas aplicaciones, desde los robots indexadores de buscadores como Google y hasta aceleradores de descargas ingenuos que buscan hacer más ágil la navegación de un usuario (siguiendo de modo preventivo todas las ligas GET de nuestro sistema para que el usuario no tenga que esperar en el momento de seleccionar alguna de las acciones en la página) van a disparar éstos eventos de manera inesperada e indiscriminada.

HTTP fue concebido (Berners-Lee, 1996) como un protocolo a través del cual se solicitaría información estática. Al implementar las primeras aplicaciones sobre HTTP, nos topamos con que cada solicitud debía incluir la totalidad del estado. En términos de redes, TCP implementa exclusivamente la capa 4 del modelo OSI, y si bien mantiene varios rasgos que nos permiten hablar tambien de /sesiones/ a nivel conexión, estas son sencillamente descartadas. Las capas 5 y superiores deben ser implementadas a nivel aplicación. HTTP es un protocolo puramente capa 6 (omite toda información relacionada con la sesión y sirve únicamente para presentar ya sea la aplicación o el contenido estático). Es por esto que los muchos sistemas Web hacen un uso extensivo de los campos ocultos (hidden) en todos sus formularios y ligas internas, transportando los valores de interacciones previas que forman parte conceptualmente de una sóla interacción distribuída a lo largo de varios formularios, o /números mágicos/ que permiten al servidor recordar desde quién es el usuario en cuestión hasta todo tipo de preferencias que ha manifestado a lo largo de su interacción.

Sin embargo, éste mecanismo resulta no sólo muy engorroso, sino que muy frágil: Un usuario malicioso o curioso puede verse tentado a modificar estos valores; es fácil capturar y alterar los campos de una solicitud HTTP a través de herramientas muy útiles para la depuración. E incluso sin estas herramientas, el protocolo HTTP es muy simple, y puede "codificarse" a mano, sin más armas que un telnet abierto al puerto donde escucha nuestro sistema. Cada uno de los campos y sus valores se indican en texto plano, y modificar el campo «user_id» es tan fácil como decirlo.

En 1994, Netscape introdujo un mecanismo denominado /galletas/ (cookies) que permite al sistema almacenar valores arbitrarios en el cliente. Éste mecanismo indica que todas las galletas que defina un servidor a determinado cliente serán enviadas en los encabezados de cada solicitud que éste le realice, por lo que se recomienda mantenerla corta — Atendiendo a esta recomendación, varias implementaciones de galletas no soportan más de 4KB. Un año más tarde, Microsoft lo incluye en su Internet Explorer; el mecanismo fue estandarizado en 1997 y extendido en el 2000 con (Kristol, Montulli, 1997) y (Kristol, Montulli, 2000). El uso de las galletas libera al desarrollador del engorro antes mencionado, y le permite implementar fácilmente un esquema verdadero de manejo de sesiones — Pero, ante programadores poco cuidadosos, abre muchas nuevas maneras de —adivinaron— cometer errores.

Dentro del cliente (típicamente un navegador) las galletas están guardadas bajo una estructura de doble diccionario — En primer término, toda galleta pertenece a un determinado servidor (esto es, al servidor que la envió). La mayor parte de los usuarios tienen configurados a sus navegadores, por privacidad y por seguridad, para entregar el valor de una galleta únicamente a su dominio origen (de modo que al entrar a un determinado sitio hostil éste no pueda robar su sesión en el banco); sin embargo, todo sistema puede solicitar galletas arbitrarias guardadas en el cliente. Para cada servidor, pueden almacenarse varias galletas, cada una con una diferente llave, un nombre que la identifica dentro del espacio del servidor. Además de estos datos, cada galleta guarda la ruta a la que ésta pertenece, si requiere seguridad en la conexión (permitiendo sólo su envío a través de conexiones cifradas), y su periodo de validez, pasado el cual serán consideradas "rancias" y ya no se enviarán. El periodo de validez se mide según el reloj del cliente.

Guardar la información de estado del lado del cliente es riesgoso, especialmente si es sobre un protocolo tan simple como HTTP. No es dificil para un atacante modificar la información enviada al servidor, y si bien en un principio los desarrolladores guardaban en las galletas la información de formas parciales, llegamos a una regla de oro: Nunca guardar información real en ellas. En vez de esto, es recomendado guardar un /token/ (literalmente, ficha o símbolo) que /apunte/ a la información. Esto es, en vez de guardar el ID de un usuario, debe enviarse una cadena /criptográficamente fuerte/ (Wikipedia, 2004-2009d) que apunte a un registro en la base de datos del servidor. ¿A qué me refiero con esto? A que tampoco grabe directamente el ID de la sesión (dado que siendo sencillamente un número, sería para un atacante trivial probar con diferentes valores hasta "aterrizar" en una sesión interesante), sino una cadena aparentemente aleatoria, creada con un algoritmo que garantice una muy baja posibilidad de colisión y un espacio de búsqueda demasiado grande como para que un atacante lo encuentre a través de la fuerza bruta.

Los algoritmos más comunes para este tipo de uso son los llamados funciones de resumen (digest) (Wikipedia, 2001-2009e). Estos generan una cadena de longitud fija; dependiendo del algoritmo, hoy en día van de los 128 a los 512 bits. Las funciones de resumen más comunes hoy en día son las variaciones del algoritmo SHA desarrollado por el NIST y publicado en 1994; usar las bibliotecas que los implementan es verdaderamente trivial. Por ejemplo, usando Perl:

use Digest::SHA1;
print Digest::SHA1->sha1_hex("Esta es mi llave");

nos entrega la cadena:

c3b6603b8f841444bca1740b4ffc585aef7bc5fa

Pero, ¿qué valor usar para enviar como llave? Definitivamente no serviría enviar, por ejemplo, el ID de la sesión - Esto nos dejaría en una situación igual de riesgosa que incluir el ID del usuario. Un atacante puede fácilmente crear un diccionario del resultado de aplicar SHA1 a la conversión de los diferentes números en cadenas (mecanismo conocido como «rainbow tables», tablas arcoíris; hay varios proyectos [10] que han construido tablas para diversas aplicaciones, como la recuperación de contraseñas en sistemas Windows). La representacíon hexadecimal del SHA1 de '1' siempre será d688d9b3d3ba401b25095389262a3ecd2ad5ad68, y del de 100 siempre será daaaa8121aa28fca0edb4b3e1f7b7c23d6152eed; el identificador de nuestra sesión debe contener elementos que varíen según algún dato no adivinable por el atacante (como la hora exacta del día, con precisión a centésimas de segundo) o, mejor aún, con datos aleatorios.

Este mecanismo nos lleva a asociar una cadena suficientemente aleatoria como para que asumamos que las sesiones de nuestros usuarios no serán fácilmente "secuestradas" (esto es, que un atacante no le atinará al ID de la sesión de otro usuario), permitiéndonos dormir tranquilos sabiendo que el sistema de manejo de sesiones en nuestro sistema es prácticamente inmune al ataque por fuerza bruta.

Como último punto: Las galletas son muchas veces vistas como un peligro por los activistas de la privacidad y el anonimato, dado que permiten crear un perfil de las páginas que va visitando un usuario (especialmente en el caso de empresas como Google o DoubleClick, que han sido especialmente exitosas en ofrecer herramientas de anuncios o de monitoreo/estadísticas a muy diversos administradores de sitios en todo el mundo). Es importante recordar que algunas personas han elegido desactivar el uso de galletas en su navegación diaria, a excepción de los sitios que expresamente autoricen. Tomen en cuenta que una galleta puede no haber sido guardada en el navegador cliente, y esto desembocará en una experiencia de navegación interrumpida y errática para dichos usuarios. Es importante detectar si, en el momento de establecer una galleta, ésta no fue aceptada, para dar la información pertinente al usuario, para que sepa qué hacer y no se encuentre frente a un sistema inoperativo más.

6. Conclusiones
------------

A lo largo de este artículo revisamos tres de las principales categorías de vulnerabilidades/errores de programación que, a juicio del autor, más prevalentes y peligrosas resultan hoy en día. Ahora bien, no es casualidad que el encabezado de cada una de las secciones correspondientes fuera «Ejemplo» — Con este artículo no implico que baste con estar conciente y alerta ante éstas amenazas para dejar de lado las demás.

La seguridad en cómputo, si bien es un campo fascinante, es un campo que requiere de constante actualización. Además, si bien éste artículo se enfoca a las vulnerabilidades más comunes en servicios Web, no es ni busca ser comprehensivos — Hay amplísimas categorías a las cuales no nos acercamos siquiera, y eso no debe leerse como que carezcan de importancia — Las tres categorías analizadas son meramente ejemplos de lo que hay que tener en mente.

Ser desarrollador de sistemas es una profesión demandante. Para cumplirla a cabalidad, requiere estar al día en lo tocante a seguridad — No estarlo significa una irresponsabilidad ante usuarios o clientes de nuestro desarrollo.

La sociedad depende más que nunca de la sistematización de los procesos y de la capacidad de sustentarlos a distancia. Los programadores, y muy en especial los programadores de sistemas en red, se han vuelto piezas fundamentales del tejido social. Es fundamental que reconozcamos y aceptemos nuestro nuevo rol responsablemente. Es fundamental que quienes nos decimos y nos sentimos desarrolladores de sistemas comprendamos la importancia de nuestro trabajo para la sociedad toda, y actuemos en consecuencia.

BIBLIOGRAFÍA

ACHOUR, Mehdi; BETZ, Friedhelm; DOVGAL, Antony et. al. (1997-2009)
PHP Manual [en línea]
Página: mysql_real_escape_string

[Consulta: 08/2009]

BERNERS-LEE, Tim (1996) The Original HTTP as defined in 1991 [en línea]

[Consulta: 08/2009]

COSTELLO, Roger (s.f.) Building Web Services the REST Way [en línea]

[Consulta: 08/2009]

ERICKSON, Cal (2002) Memory Leak Detection in Embedded Systems.
En: Linux Journal, N° 101 (sep 2002) Beltdown Media, ISSN 1075-3583

FRIEDL, Steve (2007) SQL Injection Attacks by Example [en línea]

[Consulta: 08/2009]

HEINEMEIER, David (2004-2009) ActiveRecord para Ruby [en línea]

[Consulta: 08-2009]

KRISTOL, David M and MONTULLI, Lou (1997) RFC 2109: HTTP State
Management Mechanism [en línea]

[Consulta: 08/2009]

KRISTOL, David M and MONTULLI, Lou (2000) RFC 2965: HTTP State
Management Mechanism [en línea]

[Consulta: 08/2009]

MAVITUNA, Ferruh (2007) SQL Injection Cheat Sheet [en línea]

[Consulta: 08/2009]

MICROSOFT (2009) SQL Server 2008 Product Documentation - SQL Server
2008 Books Online - Threat and Vulnerability Mitigation - SQL
Injection [en línea]

[Consulta: 08/2009]

MITRE (2008-2009a) Common Weakness Enumeration (CWE) 415: Double Free [en línea]

[Consulta: 08/2009]

MITRE (2008-2009b) Common Weakness Enumeration (CWE) 116: Improper
Encoding or Escaping of Output [en línea]

[Consulta: 08/2009]

MITRE (2008-2009c) Common Weakness Enumeration (CWE) 79: Failure to
Preserve Web Page Structure ('Cross-site Scripting') [en línea]

[Consulta: 08/2009]

MUNROE, Randall (2007): Webcomic xcdr [en línea].

[Consulta: 08/2009]

SANS/MITRE (2009) TOP 25 Most Dangerous Programming Errors [en línea]
[Consulta: 08/2009]

SHMOO GROUP (2005-2006) Rainbow Tables [en línea]

[Consulta: 08/2009]

VERISSIMO, Hamilton et. al. (2003-2009) Castle ActiveRecord [en línea]

[Consulta: 08-2009]

WHEELER, David A. (1999-2003) Secure Programming for Linux and Unix
HOWTO; capítulo 6: Avoid Buffer Overflow [en línea]

[Consulta: 08/2009]

WIKIPEDIA (2008-2009a), Not Invented Here [en línea].

[Consulta: 08/2009]

WIKIPEDIA (2004-2009b) SQL Injection [en línea].

[Consulta: 08/2009]

WIKIPEDIA (2007-2009c) Mapeos objeto-relacionales [en línea]

[Consulta: 08-2009]

WIKIPEDIA (2004-2009d) Cryptographic hash function [en línea]

[Consulta: 08/2009]

WIKIPEDIA (2001-2009e) SHA hash functions [en línea]

[Consulta: 08/2009]

WILLIAMS, Jeff, et. al. (2006-2009) Integer overflow [en línea].
OWASP: the free and open application security community

[Consulta: 08/2009]

( categories: )

Arquitecturas de paquetes al rescate

Submitted by gwolf on Fri, 06/18/2010 - 07:35
Wolf G.  2010.  Arquitecturas de paquetes al rescate. Software Gurú.

Modularización: Arquitecturas de paquetes al rescate

Convirtiendo a la hidra en nuestra aliada

En el número 27 de Software Gurú, Agustín Ramos presentó un artículo acerca de estrategias para lograr una modularización efectiva en Java. Varios de los puntos de su artículo me llevaron a dedicar la presente columna a explicar a qué llamamos una distribución de Linux, y cuál es su relación –y la solución que ofrece– a los problemas derivados de la complejidad derivada de la modularización, que bien describió Agustín.

Como deben ya saberlo, a diferencia de otros sistemas operativos, el entorno operativo completo al que normalmente nos referimos como Linux no es desarrollado por un sólo grupo, ni sigue un roadmap, criterios o visión en común. Mucho se ha argumentado acerca de las características que diferencían a los proyectos desarrollados desde planteamientos libres de aquellos desarrollados siguiendo metodologías tradicionales — No abordaré dicha discusión en este momento. Sin embargo, no quiero dejar de aprovechar la oportunidad de tener un artículo escrito por un colega en esta revista para ilustrar cómo abordamos la relación entre proyectos independientes –que logran una complejidad de miras mucho mayor que cualquier instancia de conjuntos de módulos desarrollados en casa– en el Software Libre, por qué enfatizamos tanto en lo prevalente que resulta dicha modularización, y cómo nos enfrentamos a su inherente complejidad.

Los sistemas basados en Linux son estructurados, pues, en distribuciones. Una distribución es un conjunto de programas –típicamente del órden de miles– que, entre todos, presentan la funcionalidad que un usuario espera ya no sólo de un sistema operativo sino que de todo un entorno operativo integrado. Además, se encargan de gestionar las relaciones de dependencia entre dichos programas, y resolver dichas dependencias, para facilitar al usuario la instalación, remoción y actualización de software.

Una instalación mínima de cualquier distribución de Linux no tiene menos de un par de centenares de paquetes individuales. El núcleo Linux, por sí mismo, no representa más que la interfaz más básica para presentar una abstracción del hardware ante los programas que corren sobre él. Formalmente, al mencionar Linux ni siquiera nos referimos a la interfaz de línea de comandos (misma que generalmente es provista por los paquetes bash, dash o sash, dependiendo de las necesidades del administrador). Son en realidad contados los paquetes que no dependen más que de paquetes esenciales. Toda distribución define un pequeño conjunto (decenas) de programas que es fundamental tener instalados como base para cualquier otro, conjunto mínimo que asumimos que siempre estará ahí para asegurarnos la funcionalidad mínima.

Si bien las primeras distribuciones tuvieron por objetivo facilitar la instalación de un sistema basado en Linux a usuarios con menor involucramiento técnico, y fueron instrumentales en la primer ola expansiva de usuarios que fuimos adoptando Linux hacia mediados de los 1990, han trascendido ya a éste rol para dar respuesta al infierno de dependencias al que se refiere en su texto Agustín Ramos: Hacia fines de los 1990, aproximadamente cuando todas las distribuciones comenzaban a contar en miles los paquetes independientes que ofrecían, comenzó a hacerse obvio que no bastaba con que cada paquete indicara de qué otros paquetes dependía (incluyendo, claro está, la información de versiones pertinente), sino que era necesario contar con una arquitectura de paquetes: Un esquema orientado a depósitos y no a paquetes individuales, que se encargara de resolver las dependencias cada que el administrador instalara o eliminara un paquete. La primera arquitectura fue introducida por la distribución Debian, bajo el nombre de apt: A Package Tool. Es definitivamente gracias a apt que, al día de hoy, la versión de desarrollo de Debian cuenta con 15640 paquetes fuente independientes, que resultan en 27886 paquetes binarios, cubriendo prácticamente todas las áreas de necesidad en cómputo, tanto a nivel de aplicaciones como de bibliotecas.

A diferencia de otras arquitecturas previas, como los ports de los Unixes BSD, Apt está además construido basado en el manejo de depósitos múltiples. Esto significa que, además de servirme para instalar los paquetes oficiales de la distribución, nos permite definir depósitos adicionales con el software que desarrollemos localmente, así como de paquetes adicionales que preparemos localmente para uso en nuestra organización.

Con esto como introducción, veamos cómo ésto se aplica al texto de Agustín. Por razones de espacio, me enfoco a cómo éste esquema reduce fuertemente los efectos negativos de la modularización, permitiendo a los desarrolladores crear software más robusto y temer menos a esta tan dificil de conciliar fuente de dolores de cabeza.

Demasiadas dependencias
El argumento principal mencionado en el texto al que hago referencia en contra de tener demasiadas dependencias es la posterior dificultad de instalación de nuestros sistemas. Sin embargo, al crear paquetes con la información de las dependencias, convertimos el fastidioso proceso de instalación (y todo el tiempo que requiere documentarlo) en una sóla instrucción al sistema.
Dependencias cíclicas
Coordinar el trabajo de cientos de voluntarios en todo el mundo, sin más factores de cohesión que su voluntad por crear un sistema de calidad trae como resultado natural el que parte importante de sus esfuerzos estén encaminados a la creación de documentos de políticas comprehensivos — y a que su comunidad de desarrolladores comprenda la importancia de dichos documentos.
Claro está, si la unidad atómica de trabajo en una distribución es el paquete, problemas tan claros como las dependencias cíclicas fueron de los primeros puntos en ser prohibidos por política. Sin embargo, conforme la complejidad de cada uno de los paquetes aumenta, se vuelve posible que aparezcan dependencias cíclicas de indirectas. Para evitar problemas como este, y otros destinados del control de calidad en sistemas complejos, se hacen revisiones periódicas de todos los procesos imaginables.
En este aspecto, si bien no hay balas de plata para evitar las dependencias cíclicas, contamos con una comunidad de desarrolladores verificando que estos problemas se mantengan al mínimo. No estamos sólos en el manejo de nuestros proyectos.

Largas cadenas de dependencias
Este fue precisamente el punto que motivó al desarrollo de las arquitecturas de paquetes. Podemos confiar en que la arquitectura que elijamos, empleando los depósitos del sistema y de nuestra organización, sepan resolver este punto sin que represente problemática alguna. Además, no tenemos por qué limitarnos al uso de este esquema para las dependencias de los componentes externos que empleemos. Si dentro de nuestra organización nos acostumbramos a empaquetar nuestro código en componentes, la reutilización de código que podamos hacer será muchísimo más simple y más natural.
Dependencias en conflicto
Nuevamente, aquí acudimos a la sabiduría de las masas, a la fuerza de la multitud. Una distribución basada en Linux no es sólo un conjunto de programas disponibles a través de un mismo depósito — La mayor parte del trabajo de sus creadores es asegurarse que todos los componentes sean mutuamente compatibles, y asegurar a los usuarios un producto de calidad, con todas las actualizaciones necesarias para asegurar su seguridad (cuidando no compormeter su estabilidad) durante su ciclo de vida.
Este puede ser un punto fundamental al elegir qué distribución vamos a usasr para determinado proyecto: Hay distribuciones principalmente orientadas al perfil de usuario de escritorio, para el cual es fundamental tener siempre soporte completo para el último hardware, aceleración gráfica compelta y demás bondades. Sin embargo, para basar nuestros desarrollos empresariales, típicamente preferiremos las distribuciones con ciclos de vida más largos, con un mayor soporte a largo plazo.

Como pueden ver, manejar una arquitectura de paquetes simplifica algunas de las tareas más complicadas (y más ingratas) del desarrollo de software, el manejo de toda la talacha creada por los componentes que, a fin de cuentas, incluimos para ahorrarnos trabajo.

Cuando veo los instaladores típicos, que crean enormes amasijos (típicamente en forma de enormes instaladores .msi o archivos .jar que incluyen copia de todas las dependencias para evitar estos problemas), no exagero: Me dan ganas de llorar. Porque además, al tener varias copias de una misma biblioteca en el sistema, tengo la certeza de que en caso de aparecer algún defecto en alguna de ellas, habrá componentes de mi sistema que reciban la corrección en cuestión — pero habrá otros que no. Acostumbrarse al manejo de dependencias externas nos reduce la tentación de acudir al tan temido ligado estático, reduce el peso de nuestras imágenes de instalación (y de nuestros sistemas ya instalados), y ayuda fuertemente a mantener un mayor control de calidad en nuestros procesos como un todo.

Mucha gente evita aprovechar la modularización como medida preventiva para no perder la razón resolviendo dependencias y bugs creados por compatibilidad incompleta entre versiones. Sin embargo, dejar de usar buen software, ya existente y probado por terceros, sólo por no contar con las herramientas de seguimiento correctas es un triste error en el que debemos evitar caer. La hidra de la modularización a la que se refiere Agustín puede ser un mounstro mortal, pero si aprendemos a hablar con ella puede ser nuestro mayor aliado. Porque si dos cabezas piensan mejor que una, ¿qué decir de decenas de cabezas?

( categories: )

Debian: Su ubicación en el universo de proyectos de Software Libre, y qué emerge de ella

Submitted by gwolf on Thu, 05/13/2010 - 13:04
Written in...: 
2010

What characterizes the Debian project? What differentiates it from other distributions? What characteristics emerge from these points? I cover:

  • What is Debian?
  • Relation between Debian and Free Software
  • Main motivators for its developers
  • Relation between this project and other developments
  • Release cycles and quality assurance

I prepared this talk for Quito, Ecuador, May 2010.

Resumen: 

¿Qué caracteriza a Debian como proyecto? ¿Qué lo diferencía de otras distribuciones? ¿Qué características emergen de estos puntos? Analizo:

  • ¿Qué es Debian?
  • Relación de Debian con el Software Libre
  • Motivación primaria de sus desarrolladores
  • Relación del proyecto respecto a otros desarrollos
  • Ciclos de liberación y control de calidad

Preparé esta charla para presentarla en Quito, Ecuador, en mayo de 2010.

( categories: )

Voto electrónico

Submitted by gwolf on Tue, 01/26/2010 - 16:46
Wolf G.  2010.  Voto electrónico. Software Gurú.

Nota: Este artículo lo comencé a trabajar para publicarlo como parte del trabajo del Seminario Construcción Colaborativa del Conocimiento. Al ver que era pertinente e indicado para su inclusión en mi columna para la revista SoftwareGurú, lo utilicé ahí también — Pero tuve que reducirlo a menos de la mitad de su extensión original. Aquí lo presento completo, en una versión muy similar a la que será publicada como resultado del Seminario, refiriendo hacia aquí a los lectores interesados de la revista. Como archivos adjuntos (al final) encontrarán también la versión de SoftwareGurú.

Voto electrónico

La postura que ante las votaciones electrónicas han tomado diversos grupos relacionados con la creación y escrutinio de software y de procesos sociales ilustra muy bien varios de los puntos delineados en otros capítulos de la presente obra. Este capítulo formaba parte originalmente del capítulo «Software Libre y Construcción Democrática de la Sociedad», e ilustra uno de los puntos y de las maneras en que las comunidades de creación de conocimiento tanto de seguridad en cómputo como de Software Libre han abordado un punto de gran importancia para la vida en sociedades democráticas actuales, insertándose en el entorno político imperante. Hemos decidido, tanto por la extensión como por la relación de este tema con varios otros de los presentados en esta obra, hacer del presente apartado un capítulo independiente.

Los promotores de las diferentes vertientes del Conocimiento Libre son los primeros en recalcar los tremendos fallos –conceptuales y de implementación– que hacen que las estaciones computarizadas de emisión y contabilización de votos sean, desde su planteamiento, una causa perdida [1] — Ninguna de las numerosas implementaciones a la fecha han salido airosas ante el escrutinio (incluso casual) de expertos en seguridad [2], a veces con resultados verdaderamente nefastos [3], [4]. Los escrutinios generalmente han sido dirigidos por grupos de activistas independientes buscando señalar las deficiencias del proceso, con la muy notable excepción del ejemplo puesto por el Tribunal Superior Electoral de Brasil, al cual abordaremos más adelante.

Obviamente, estos resultados no son del agrado de las compañías que buscan vender máquinas supuestamente seguras, diseñadas ex-profeso para el conteo de votos. Se han dado a conocer incluso amenazas hechas contra dichos equipos de investigadores [5] por desarrollar estos trabajos. En este caso, la demanda es que, en asuntos tan sensibles, relevantes e intervenibles como la vida democrática, es sencillamente imposible asegurar los elementos básicos de confiabilidad y auditabilidad.

Diversos argumentos han sido esgrimidos a favor del voto electrónico, pero pueden ser resumidos en tres:

  • Disminución de costos: Un adecuado proceso democrático es caro. La papelería electoral debe ser impresa con mecanismos suficientes para asegurar su unicidad, deben proveerse mecanismos para garantizar que sólo los electores autorizados emitan su voto, y debe haber garantías de no manipulación para todos los componentes involucrados en el proceso. La automatización del proceso ayuda a implementar estos candados a un menor costo.
  • Agilidad en la obtención de resultados: No hay nada que genere mayor falta de confianza y suspicacia en los procesos que una demora en la publicación de los resultados. Se ha argumentado que a través del voto electrónico, los resultados pueden ser anunciados prácticamente de inmediato tras haberse cerrado la casilla.
  • Confiabilidad de los actores: La experiencia de muchos países en torno a los fraudes electorales apunta dolorosamente a la falta de integridad de los actores involucrados en el proceso — Personas susceptibles ya sea a la compra de concienicas, a la extorsión, o directamente a la violencia física; si todo el proceso es controlado por computadoras, éstos factores deberían perder peso.

En las siguientes secciones analizamos por qué los tres argumentos caen por su propio peso.

Agilidad en la obtención de resultados

Una de las principales obsesiones de la sociedad actual es la velocidad del acceso a la información. Los medios electrónicos de comunicación y el uso de Internet nos han acostumbrado a que la información debe estar disponible tan pronto ocurren los hechos, y debe llegar a toda la sociedad tan pronto está disponible.

Los sistemas electorales en general estipulan que, para no manipular los resultados de una elección en proceso, no deben darse a conocer sus resultados parciales hasta que haya cerrado la última de las urnas – No hacerlo de esta manera significaría que la tendencia influiría en los resultados de muchas maneras indeseables. Sin embargo, una vez que cierra ésta última urna, en la mayor parte de las democracias modernas hay un periodo típicamente de un par de horas en que es necesario esperar a que las autoridades electorales recopilen la información generada por típicamente decenas de miles de casillas y den a conocer el resultado. Hay una gran presión por parte de los ciudadanos, y muy especialmente de los medios, para que las autoridades electorales publiquen los resultados de inmediato. Además del apetito por la información expedita, ésto viene fundamentado en ejemplos de ocultamientos de información que eran realizados conforme los números comenzaban a fluir — Ejemplo de esto son las declaraciones que hizo veinte años más tarde Manuel Bartlett Díaz, quien fuera en 1988 Secretario de Gobernación y presidente de la Comisión Federal Electoral durante las muy cuestionadas elecciones presidenciales de 1988 [6]: La decisión de no dar a conocer datos preliminares fue tomada por el presidente Miguel de la Madrid, dado que, cito: si se oficializaba en ese momento –con datos parciales– que Cárdenas Solórzano iba ganando, al final nadie aceptaría un resultado distinto.

En la experiencia mexicana, la situación ha cambiado radicalmente de la imperante hace tan sólo dos décadas, como claro resulado de las frecuentes acusaciones de fraude electoral que nuestro sistema electoral ha sufrido — En vez de una demora cercana a una semana, el Instituto Federal Electoral y las autoridades correspondientes de cada uno de las entidades federativas publican los resultados de las encuestas de salida y los conteos rápidos típicamente dentro de las dos primeras horas tras haber concluído la votación, siempre que haya suficiente márgen estadístico para no causar confusión en la población.

Impulsar una solución con tantos riesgos como una urna electrónica para ganar como tope estas dos horas sencillamente no tiene sentido. Además, el tiempo invertido por los funcionarios electorales en cada casilla en el conteo de votos emitidos es sólo una fracción del dedicado a las tareas de verificación y protocolización que deben llevarse a cabo antes de declarar concluída una elección. Sumando ésto a que –por consideraciones de seguridad– las estaciones de voto no están pensadas para contar con conectividad a red (y que ni los países más industrializados cuentan con una cobertura de Internet del 100% de su territorio), por lo cual debe haber forzosamente un paso manual de comunicación de resultados al centro de control de la autoridad electoral, el argumento de reducción de tiempos queda descartado.

Federico Heinz cierra su texto «¿El voto electrónico mejora la democracia?» [7] con la siguiente idea:

Una alternativa factible es realizar la votación mediante formularios que contengan a todos los partidos, dejar que los votantes marquen su elección con tinta, y usar un scanner óptico para hacer un escrutinio automático, verificable mediante un simple recuento manual. No hay nada en contra de un escrutinio electrónico, pero digitalizar el acto mismo de la emisión del voto es extremadamente peligroso para la democracia.

El uso de boletas a papel y tinta aptas para ser scanneadas por equipo de reconocimiento óptico puede ser la opción más adecuada en este sentido. Permite la verificación de cientos de boletas en apenas un par de minutos, y permite conservar todos los atributos positivos del sistema tradicional.

Confiabilidad de los actores

Algunos proponentes del voto electrónico mencionan que con el voto tradicional en papel todos estos fraudes siempre han existido1, y que éste no agrava los riesgos — Sin embargo, más que reducir las posibilidades de los agentes fraudulentos, al implementar el voto electrónico estaríamos aumentando la profundidad a la que podrían llegar, e imposibilitando cualquier acción de auditoría o rendición de cuentas.

La votación electrónica tiene muchas modalidades y muchas aristas. En líneas generales, y contrario a lo que muchos esperarían, los expertos en seguridad informática y los activistas sociales involucrados en esta lucha no recomiendan exigir que las urnas electrónicas estén basadas en Software Libre para su funcionamiento, sino que sencillamente recomiendan en contra de su utilización. Citando a Heinz, [7]:

El mecanismo de auditar completamente el funcionamiento de las urnas es impracticable. Esta es una tarea que sólo podría ser ejecutada por una elite de especialistas, de los que hay muy pocos en el mundo, y requiere la cooperación de las empresas que proveen las urnas así como de todos sus proveedores. Y aún si consiguiéramos todo eso, la eficacia de una auditoría sería más que dudosa: no sólo debemos garantizar que todo el software es correcto (lo que es imposible), sino que además debemos verificar que el software presente en las urnas el día de la elección es idéntico al auditado, tarea que nuevamente requiere de especialistas. ¿Y por qué hemos de confiar en los especialistas, si no queremos confiar en sacerdotes ni en empresas? Una de las muchas virtudes del "anticuado" sistema de escrutino tradicional es que cualquier persona que sepa leer, escribir y hacer operaciones de aritmética elemental está en condiciones de controlarlo. Esta es una característica esencial y no debemos renunciar a ella.

Uno de los más interesantes argumentos que ilustran por qué las urnas electrónicas carecen inherentemente de confiabilidad es el presentado —sin aplicarlo en éste ramo específico— por Ken Thompson en 1983 [8], en su discurso al recibir el Premio Turing de la ACM2. Thompson hace una sencilla demostración de por qué un sistema que llega al usuario final (y esto es mucho más cierto hoy en día que en 1983, en que los lenguajes y marcos de desarrollo utilizados suben increíblemente en la escala de la abstracción comparado con lo existente entonces) es prácticamente imposible de auditar por completo un programa, ni siquiera teniendo su código fuente, ni siquiera teniendo el código fuente del compilador. Traduciendo de las conclusiones de Thompson:

La moraleja es obvia. No puedes confiar en el código que no creaste tú mismo. (Especialmente código proveniente de compañías que emplean a gente como yo). No hay un nivel suficiente de verificación o escrutinio de código fuente que te proteja de utilizar código no confiable. En el proceso de demostrar la posibilidad de este tipo de ataque, elegí al compilador de C. Podría haber elegido a cualquier programa que manipule a otros programas, como al ensamblador, cargador, o incluso microcódigo embebido en el hardware. Conforme el nivel de programación se vuelve más bajo, éstos fallos se volverán más y más difíciles de detectar. Esta vulnerabilidad bien instalada en microcódigo será prácticamente imposible de detectar.

Éste argumento ha sido clave para llegar a conclusiones como la adoptada en marzo del 2009 por la Corte Suprema de Alemania [9],[10]:

Un procedimiento electoral en el que el elector no puede verificar de manera confiable si su voto fue registrado sin falsificación e incluido en el cálculo del resultado de la elección, así como comprender cabalmente de qué manera los votos totales emitidos son asignados y contados, excluye del control público a componentes centrales de la elección, y por lo tanto no alcanza a satisfacer las exigencias constitucionales.

El punto de la confiabilidad es el que más fervientemente se sigue debatiendo. El caso brasileño resulta muy esperanzador: A diferencia de la mayor parte de los gobiernos de países supuestamente desarrollados, en Brasil la tecnología utilizada para el voto electrónico está completamente basada en tecnología desarrollada localmente, empleando software libre. En noviembre del 2009, el Tribunal Superior Electoral brasileño convocó a la comunidad de seguridad a encontrar vulnerabilidades sobre las estaciones receptoras de votos, a cambio de una recompensa económica para los mejores análisis[11]. Dentro de los términos estipulados, sólo uno de los participantes (Sergio Freitas da Silva) logró su propósito [12]. Y si bien no logró vulnerar los resultados de éste sistema, sí logró –mediante un monitoreo de las radiaciones electromagnéticas– averiguar por quién emitía su voto cada uno de los electores, rompiendo el principio de secrecía electoral, empleando únicamente equipo casero de bajo costo al buscar que esto fuera meramente una prueba de concepto; un atacante determinado podría utilizar equipo mucho más sofisticado para intervenir las votaciones a mucha mayor distancia.

Y si bien el sistema empleado por Brasil sale mucho mejor parado que los empleados en Europa y Estados Unidos, no debemos tomar la ausencia de evidencia por evidencia de ausencia: Lo único que demostraron es que ninguno de los atacantes pudo demostrar una vulnerabilidad en el periodo estipulado, o no quiso hacerlo por el precio ofrecido, pero nada indica que no haya fallas no encontradas — O peor aún, puertas traseras intencionales.

Disminución de costos

La sociedad está acostumbrada a lidiar con los bemoles del voto tradicional, utilizando al papel como su medio primario. Una crítica muy común a éstos procesos, especialmente en los países cuyas democracias no están bien consolidadas (y por tanto, requieren de mucho mayor inversión tanto en la vigilancia como en la promoción de la participación de las elecciones) es el costo — En México, citando a un caso extremo (el sistema electoral más caro de América Latina [13]), cada sufragio emitido en las elecciones intermedias del 2009 tuvo un costo superior a los 17 dólares, aunque hay estimaciones que lo llegan a ubicar en hasta 50 dólares, tomando en cuenta gastos ocultos.

Como mencionamos anteriormente, un rubro que en el sin duda podrían presentarse importantes ahorros es en la generación, el manejo y la custodia del material electoral. Sin embargo, como queda demostrado tras el estudio realizado por Feldman, Halderman y Felten a las estaciones de votación Diebold AccuVote-TS [14], las más difundidas en los Estados Unidos y que han sido responsables de la recopilación de votos de hasta el 10% de los electores de dicho país, con conocimiento técnico especializado éstas máquinas presentan un nivel de confiabilidad ante ataques verdaderamente bajo, y permiten —requiriendo de un tiempo mínimo de acceso— la reprogramación resultando en resultados fraudulentos que serían prácticamente imposibles de lograr en una elección tradicional sin recurrir a métodos violentos.

Las vulnerabilidades descritas por Feldman, Halderman y Felten no son privativas a los equipos Diebold — En el sitio Web en el cual está publicado su artículo junto con un video de diez minutos demostrando su ataque y una lista de preguntas frecuentes mencionan: (traducido)

¿Por qué estudiaron éstas máquinas Diebold? ¿Por qué no otras tecnologías para votos?

Estudiamos estas máquinas porque son las que conseguimos. Si hubiésemos tenido acceso a otro tipo de máquinas, probablemente las hubiéramos estudiado.

¿Son otras máquinas más seguras que las que estudiaron?

No lo sabemos. Esperamos que así lo sean —las elecciones dependen ya de ellas— pero no hay suficiente evidencia para responder a esta pregunta

Un rastro impreso verificado por cada votante es la protección más importante que puede hacer más seguras a las máquinas de voto electrónico.

El último punto mencionado es de especial relevancia: Un rastro impreso verificado por cada votante. La única garantía que un votante puede tener de que su voto fue registrado correctamente es que el sistema genere una boleta impresa y de caracter irrevocable, misma que sea verificada por el votante al instante, la cual se convertirá en el documento probatorio de la elección3. No hay manera —ver la cita de [8] en la sección Confiabilidad de los actores— de que el estado interno de una computadora sea confiable, y muchísimo menos cuando hablamos del proceso más importante y más sensible de la vida política de un país.

Llegamos entonces a una contradicción: El equipo de votación no es barato, en términos absolutos. Su adquisición por parte de un gobierno o ente de autoridad podría justificarse si se plantea prorratear a lo largo de varias elecciones — pero si éste tiene que estar sujeto a una estricta vigilancia contínua, incluso en los años en que no será utilizado. Debe recibir mantenimiento, y debe abastecerse con una cantidad no despreciable de insumos, para asegurar un rastro impreso verificado. Además, en caso de sufrir un desperfecto, todas las casillas deben tener un plan de respaldo: Casi indefectiblemente, esto significaría tener papelería tradicional para enfrentar desde un desperfecto del equipo hasta un sabotaje, por ejemplo, en el suminstro eléctrico. Por tanto, el supuesto ahorro puede volverse en contra nuestra, convirtiéndose en un gasto mucho mayor al que implican las votaciones tradicionales.

Referencias

  • [1.] ¿El voto electrónico mejora la democracia?, Heinz, Federico , (2006)
  • [2.] Evidence of New Jersey Election Discrepancies, Felten, Ed , (2008)
  • [3.] Are Your Votes Really Counted? Testing the Security of Real-world Electronic Voting Systems, Balzarotti, D., Banks G., Cova M., Felmetsger V., Kemmerer R., Robertson W., Valeur F., and Vigna G. , International Symposium on Software Testing and Analysis, 20/07/2008, Seattle, WA, (2008)
  • [4.] Evaluating the Security of Electronic Voting Systems, Balzarotti, D., Banks G., Cova M., Felmetsger V., Kemmerer R., Robertson W., Valeur F., and Vigna G. , The Computer Security Group at UCSB, (2008)
  • [5.] Interesting Email from Sequoia, Felten, Ed , (2008)
  • [6.] De la Madrid me ordenó no informar que Cárdenas iba ganando, asegura Bartlett, , La Jornada, 2008/07/03, Volume 2008, Mexico, (2008)
  • [7.] ¿El voto electrónico mejora la democracia?, Heinz, Federico , (2006)
  • [8.] Reflections on trusting trust, Thompson, Ken , Communications of the ACM, 08/1984, Volume 27, Number 8, p.761-763, (1984)
  • [9.] Beim Einsatz elektronischer Wahlgeräte müssen die wesentlichen Schritte der Wahlhandlung und der Ergebnisermittlung vom Bürger zuverlässig und ohne besondere Sachkenntnis überprüft werden können., BVerfG , p.paragraph 1-163, (2009)
  • [10.] Alemania: urnas electrónicas anticonstitucionales, Heinz, Federico , 2009/03/06, Volume 2009, Number 2009/03/06, (2009)
  • [11.] Teste de segurança do sistema eletrônico de votação, , Volume 2009, Number 2009/12/29, (2009)
  • [12.] Un investigador logra violar el secreto del voto en las urnas brasileñas, Busaniche, Beatriz , Voto electrónico, Volume 2009, Number 2009/12/29, Buenos Aires, Argentina, (2009)
  • [13.] Cuesta el voto en México 18 veces más que el promedio en AL, dicen expertos, Urrutia, Alonso, and Martínez Fabiola , La Jornada, 2009/06/19, (2009)
  • [14.] Security Analysis of the Diebold AccuVote-TS Voting Machine, Feldman, Ariel J., Halderman Alex J., and Felten Ed , 2007 USENIX/ACCURATE Electronic Voting Technology Workshop (EVT’07), 08/2007, (2007)
  • 1. p.ej. http://seminario.edusol.info/resena/beatriz-ramirez/2009/03-1 (requiere registro)
  • 2. Premio al que comunmente se hace referencia como el nóbel del cómputo
  • 3. Y claro está, es fundamental que cada una de estas boletas sea generada por separado, recortada de la inmediata anterior y posterior, con garantía de que no haya un patrón seguible en el corte, para garantizar el anonimato del elector
( categories: )

Codificación de Caracteres

Submitted by gwolf on Thu, 12/03/2009 - 19:27
Wolf G.  2009.  Codificación de Caracteres. Software Gurú. :48-49.

NOTA: Al preparar este texto, excedí el límite de espacio que me asignan, por lo cual después de terminar de escribir tuve que ver qué párrafos eran más prescindibles. Acá reproduzco el artículo completo, y adjunto como archivo la versión tal cual apareció impresa.

Hace un par de días recibí uno de esos correos que parecen venir de una época ya superada y olvidada hace años: Un correo que comenzaba con la frase:

Para evitar problemas de compatibilidad, este correo no incluye acentos ni enies

En efecto, este problema casi ha desaparecido del correo, y ese pretexto sencillamente ya no resulta ni siquiera una excusa adecuada para quien le da flojera escribir el español correctamente.

Sin embargo, no en todos los campos podemos hablar de un éxito tan grande como en el manejo del correo electrónico — Y es de esperarse. La naturaleza misma del correo lo requiere. Cada mensaje individual debe ser transportado, sin que le sean inflingidas modificaciones por agentes externos, entre los equipos de los participantes en una conversación — que pueden estar configuradas con referentes culturales completamente distintos.

Y si bien para los hispanoparlantes, especialmente para aquellos que sienten que el uso de símbolos internacionales (acentos, tildes y signos de apertura de interrogación/exclamación) es opcional, el correo funcionó casi bien desde un principio (y a continuación detallaremos el por qué), cuando el uso de Internet dio su gran salto cuantitativo en los 1990s y llegó a la población en general, para los nativos de muchas otras culturas alrededor del mundo se hizo imperativo encontrar cómo comunicarse confiablemente.

Pero el problema viene de mucho más atrás. Repasemos rápidamente la historia de los esquemas de codificación de caracteres.

Repaso histórico

La codificación a partir de la cual se originan todos los esquemas en uso hoy en día nació en 1963, revisado/ratificado en 1967 con el nombre de ASCII: Código Estándar Americano para el Intercambio de Información. ASCII es un código de 7 bits, permitiendo la representación de hasta 128 caracteres, y en su versión definitiva incluye 32 caracteres de control, 34 símbolos, 52 caracteres de texto (mayúsculas y minúsculas) y 10 dígitos.

Sobra decir que el ámbito cómputo ha cambiado drásticamente desde 1963. La computadora debía representar apenas la información indispensable para ser comprendida por sus operadores, y en la propuesta original, ASCII no incluía ni siquiera letras minúsculas . Pero ya en 1964 aparecieron las máquinas de escribir IBM MT/ST: Una máquina de escribir electrónica, con la capacidad de guardar (y corregir) páginas en cinta magnética. Fue sólo cuestión de tiempo (y del necesario paso de popularización que siguió a la revolución de las computadoras personales hacia fines de los 1970) para que estas capacidades quedaran al alcance de todo mundo.

Y es ahí donde se hizo obvio que haría falta extender ASCII: Todos los idiomas europeos que utilizan el alfabeto latino a excepción del inglés requieren de diferentes tipos de diacríticos para ser representados; tras varias ideas descartadas, se aprovechó el hecho de que hacia fines de los 1970 todas las computadoras ampliamente desplegadas tenían un tamaño de palabra de 8 bits para utilizar un ASCII ampliado que daría 128 caracteres adicionales. Sin embargo, la idea resonó rápidamente… Y no surgió un estándar para su uso. Además, muchos de estos caracteres fueron empleados para incluir caracteres gráficos, para permitir construir interfaces amigables al usuario.

En 1981, IBM puso a la venta su primer computadora personal - La IBM 5051, o como se popularizó, la PC. Entre sus características contaba con una tarjeta de video con páginas de códigos reprogramables — La mitad superior del espacio de caracteres podía ser definida por software; los caracteres cargados por omisión eran los de la página de códigos 437 (CP437), con soporte parcial para algunos lenguajes europeos, pero –debido al espacio empleado por los caracteres semigráficos para representar interfaces al usuario– nunca fueron suficientes, por lo que en general era necesario activar una página de código alternativa — Para el español, la CP850. La situación mejoró al popularizarse los entornos gráficos y dejar de depender de los caracteres semigráficos; varias hojas de código relacionadas pudieron agruparse en un menor número — En este caso, para lenguajes europeos occindentales, la ISO-8859-1.

El problema se presenta al intercambiar archivos con usuarios de otras páginas: Los datos que usan una página son indistinguibles que los que usan otra. Si compartiera un archivo ISO-8859-1 con una persona de Europa oriental (ISO-8859-2), los caracteres acentuados aparecerían modificados, aparentemente corruptos. La situación de los lenguajes de Asia oriental era mucho peor aún, dado que por la cantidad de glifos, plantear el uso de un alfabeto de 256 caracteres resultó imposible — y por muchos años, la interoperabilidad fue meramente un sueño.

En 1988, Joe Becker, Lee Collins y Mark Davis, de Xerox y Apple, se reunieron para atacar este problema de raiz: Lanzaron la iniciativa del sistema Unicode, buscando aprovechar los grandes avances de más de 20 años del cómputo para lograr un conjunto de caracteres apto para todo el mundo. Pronto su iniciativa logró captar la atención y el respaldo de otros líderes del desarrollo del cómputo.

El desarrollo de Unicode no está libre de desaciertos y peleas políticas, pero el resultado bien lo valió: Para 1996 se publicó la especificación Unicode 2.0, permitiendo un espacio de más de un millón (216+220) de puntos de código independientes, reteniendo compatibilidad hacia atrás completa con el principal esquema heredado (ISO-8859-1), derivado de CP850.

Unicode es tan grande que su representación interna no está libre de polémica e interpretaciones. Sin entrar en detalles técnicos, las dos principales representaciones son UTF-8 (utilizada en sistemas basados en Unix, y en general, para toda transmisión sobre redes de datos) y UTF-16, descendiente de UCS-2 (en uso principalmente en sistemas Windows).

No entraré mucho en detalles respecto a estas dos representaciones — Es sólo importante estar conscientes de que una cadena Unicode puede estar representada internamente de diferentes maneras; UTF-8 está basado en elementos individuales de 8 bits, mientras que el átomo en UTF-16 es de 16 bits, por lo que –especialmente con idiomas basados en el alfabeto latino– UTF-8 es más compacto (con UTF-16, el byte superior consistirá sólamente de ceros) y es sensiblemente más robusto para transmisiones sobre la red (una corrupción de datos afecta un punto mínimo, mientras que con UTF-16 puede hacer ilegible todo el texto a partir de ese punto).

Un punto importante a mantener en cuenta es que la representación binaria de texto ASCII heredado es idéntica a su representación en UTF-8 — No así con UTF-16. Para más detalles respecto a estas dos representaciones, así como las otras varias posibles, sugiero leer (Wikipedia Comparación).

¿Qué hace diferente al tratamiento de Unicode?

Hasta antes de Unicode, todo lo que teníamos que saber respecto a un esquema de codificación se limitaba a elegir la salida adecuada, o interpretar la entrada como debía ser. Y Unicode agrega muchos puntos a la mezcla. algunos de ellos:

Longitud de una cadena
Como programadores, estamos acostumbrados a que la longitud caracteres de una cadena es igual a su tamaño entes. Unicode rompe con este supuesto — Para medir langitud ahora tenemos que evaluar la cadena completa ycontrar cuáles partículas son de qué tamaño — Un caracterede medir entre uno y seis bytes, y hay además caracteresm>combinantes, que no ocupan un espacio por sí sólosno que modifican al anterior.
No todas las cadenas son válidas
Con los esquemas tradicionales de 8 bits, todo conjunto deracteres es válido. Al hablar de las representaciones deicode, hay cadenas que resultan inválidas.

Si han visto una página Web donde los caracteres con diacríticos aparecen substituídos por caracteres en forma de rombo con un signo de interrogación dentro (�, punto de código U+FFFD), éste caracter denota que el procesamiento de Unicode no pudo completarse. Sin embargo, no todas las aplicaciones son tan benignas como un navegador — Una base de datos, por ejemplo, debe negarse a guardar datos mal-formados, por lo cual debemos estar conscientes del tipo de excepciones que posiblemente recibiremos si nuestro usuario nos da datos inválidos.

Caracteres definidos semánticamente, no visualmente
Los caracteres en Unicode no buscan únicamente representar un grupo de grafías, sino que su significado. En tiempos del ASCII nos acostumbramos a utilizar la representación gráfica más cercana a un caracter. Por ejemplo, nos acostumbramos a representar a la multiplicación con el asterisco ya sea con la letra «x» o con el asterisco; con Unicode podemos usar –cual debe ser– el símbolo ✕ (U+2715). Los alemanes ya no se ven forzados a usar la β (beta griega, U+0392) en vez de la ß (Eszett o S fuerte, U+00DF).
Si bien esto es más una ventaja que nada, puede llevarnos a confusiones. Por ejemplo, si bien en ASCII con CodePage 850 sólo podemos representar a una letra acentuada de una manera (la letra á está en la posición 160), en Unicode puede representarse en este mismo punto, o como la combinación de una a minúscula (caracter 95, U+0061) y un caracter combinante de acento agudo (U+0300): á. Esto permite la gran expresividad que requieren algunos idiomas o alfabetos (ver (Wood 1999-2009) con una clara ejemplificación de la expresividad que otorgan), pero nos obliga a considerar más casos especiales al enfrentarnos a Unicode desde el punto de vista del desarrollador de sistemas.

Promesas, promesas…

Como ya lo hemos discutido en este mismo espacio, el punto que más debemos cuidar como desarrolladores son los puntos de cruce — Las barreras, las costuras en la realidad. Lo ideal sería que no hiciera falta que un componente le dijera explícitamente al otro que está listo y dispuesto para usar Unicode. Un sistema operativo instalado limpiamente el día de hoy, así como casi cualquier aplicación que instalemos sobre de éste, debe funcionar bien –y de manera predeterminada– con Unicode. Claro está, la realidad siempre se interpone — Por un lado, podemos tener que interactuar con componentes heredados. Por otro lado, no podemos olvidar el hecho de que muy rara vez podemos controlar el entorno de nuestros usuarios — Y por último, un punto donde nos toparemos ineludiblemente con problemas de codificación es al hablar con dispositivos externos.

Si bien es posible aplicar reglas heurísticas para determinar si una cadena es válida o no como UTF-8 (Dürst 1997), prácticamente cualquier protocolo que utilicemos hoy en día para transmitir contenido que deba ser interpretado como texto proporciona un campo en su encabezado para indicar el tipo de contenido a utilizar — Sin olvidar tomar en cuenta que muchos protocolos contemplan la transmisión de datos multipartes, ¡y cada una de las partes puede llevar codificaciones diferentes!

Conclusiones

Viendo sólamente estos puntos, puede que muchos de ustedes los vayan poniendo en la balanza, y les parezca que migrar a Unicode es demasiado dificil y no vale la pena. Sin embargo, sencillamente, no nos queda opción: El mundo entero va avanzando en ese sentido.

Si bien nos toca la parte más simple, por ser nativos de una cultura basada en el alfabeto dominante en Internet, no podemos ignorar que formamos parte de un mundo plenamente globalizado, y que los días de vivir en nuestra islita feliz de un sólo alfabeto han terminado.

Del mismo modo que sencillamente ya no es aceptable que una aplicación falle cuando le enviamos datos escritos correctamente acentuados, tampoco es aceptable que rechacemos registrar a personas cuyo nombre tiene origen extranjero, o que tienen una dirección de destinatario no representable con caracteres latinos. Ni es aceptable, en un mundo que tiende a la presentación de software como servicio «en la nube», no ofrecer una solución capaz de manejar los datos enviados por usuarios de todo el mundo es ofrecer una solución con baja calidad de servicio.

Referencias

( categories: )

Manteniendo el estado en nuestras aplicaciones Web: Una historia de galletas y de resúmenes

Submitted by gwolf on Fri, 08/28/2009 - 08:05
Wolf G.  2009.  Manteniendo el estado en nuestras aplicaciones Web: Una historia de galletas y de resúmenes. Software Gurú.

Una grandísima proporción de los sistemas desarrollados hoy en día, siguen el paradigma cliente-servidor. Y si bien hay muy diferentes maneras de implementar sistemas cliente-servidor, indudablemente la más difundida hoy por hoy es la de los sistemas Web.

La conjunción de un protocolo verdaderamente simple para la distribución de contenido (HTTP) con un esquema de marcado suficientemente simple pero suficientemente rico para presentar una interfaz de usuario con la mayor parte de las funciones requeridas por los usuarios (HTML) crearon el entorno ideal para el despliegue de aplicaciones distribuídas.

Desde sus principios, el estándar de HTTP menciona cuatro verbos por medio de los cuales se puede acceder a la información: GET (solicitud de información sin requerir cambio de estado), POST (interacción por medio de la cual el cliente manda información compleja y que determinará la naturaleza de la respuesta), PUT (creación de un nuevo objeto en el servidor) y DELETE (destrucción de un determinado objeto en el servidor). Sin embargo, por muchos años, éstos fueron mayormente ignorados — La mayor parte de los sistemas hace caso omiso a través de qué verbo llegó una solicitud determinada; muchos navegadores no implementan siquiera PUT y DELETE, dado su bajísimo nivel de uso — Aunque con la popularización del paradigma REST, esto probablemente esté por cambiar.

El protocolo HTTP, sin embargo, junto con su gran simplicidad aportó un gran peligro — No una vulnerabilidad inherente a los sistemas Web, sino que un peligro derivado de que muchos programadores no presten atención a un aspecto fundamental de los sistemas Web: Cómo manejar la interacción repetida sobre de un protocolo que delega el mantener el estado o sesión a una capa superior. Esto es, para un servidor HTTP, toda solicitud es única. En especial, un criterio de diseño debe ser que toda solicitud GET sea idempotente — Esto significa que un GET no debe alterar de manera significativa1 el estado de los datos.

HTTP fue concebido [1] como un protocolo a través del cual se solicitaría información estática. Al implementar las primeras aplicaciones sobre HTTP2, nos topamos con que cada solicitud debía incluir la totalidad del estado. Es por esto que los muchos sistemas Web hacen un uso extensivo de los campos ocultos (hidden) en todos sus formularios y ligas internas, transportando los valores de interacciones previas que forman parte conceptualmente de una sóla interacción distribuída a lo largo de varios formularios, o números mágicos que permiten al servidor recordar desde quién es el usuario en cuestión hasta todo tipo de preferencias que ha manifestado a lo largo de su interacción.

Sin embargo, éste mecanismo resulta no sólo muy engorroso, sino que muy frágil: Un usuario malicioso o curioso (llamémosle genéricamente «atacante») puede verse tentado a modificar estos valores; es fácil capturar y alterar los campos de una solicitud HTTP a través de herramientas muy útiles para la depuración. E incluso sin estas herramientas, el protocolo HTTP es muy simple, y puede "codificarse" a mano, sin más armas que un telnet abierto al puerto donde escucha nuestro sistema. Cada uno de los campos y sus valores se indican en texto plano, y modificar el campo «user_id» es tan fácil como decirlo.

En 1994, Netscape introdujo un mecanismo denominado galletas (cookies) que permite al sistema almacenar valores arbitrarios en el cliente3. Un año más tarde, Microsoft lo incluye en su Internet Explorer; el mecanismo fue estandarizado en 1997 y extendido en el 2000 con los RFCs 2109 y 2965 [2]. El uso de las galletas libera al desarrollador del engorro antes mencionado, y le permite implementar fácilmente un esquema verdadero de manejo de sesiones — Pero, ante programadores poco cuidadosos, abre muchas nuevas maneras de —adivinaron— cometer errores.

Dentro del cliente (típicamente un navegador) las galletas están guardadas bajo una estructura de doble diccionario — En primer término, toda galleta pertenece a un determinado servidor (esto es, al servidor que la envió). La mayor parte de los usuarios tienen configurados a sus navegadores, por privacidad y por seguridad, para entregar el valor de una galleta únicamente a su dominio origen (de modo que al entrar a un determinado sitio hostil éste no pueda robar nuestra sesión en el banco); sin embargo, nuestros sistemas pueden solicitar galletas arbitrarias guardadas en el cliente. Para cada servidor, podemos guardar varias galletas, cada una con una diferente llave, un nombre que la identifica dentro del espacio del servidor. Además de estos datos, cada galleta guarda la ruta a la que ésta pertenece, si requiere seguridad en la conexión (permitiendo sólo su envío a través de conexiones cifradas), y su periodo de validez, pasado el cual serán consideradas "rancias" y ya no se enviarán. El periodo de validez se mide según el reloj del cliente.

Guardar la información de estado del lado del cliente es riesgoso, especialmente si es sobre un protocolo tan simple como HTTP. No es dificil para un atacante modificar la información que enviaremos al servidor, y si bien en un principio los desarrolladores guardaban en las galletas la información de formas parciales, llegamos a una regla de oro: Nunca guardar información real en ellas. En vez, guardemos algo que apunte a la información. Esto es, por ejemplo, en vez de guardar el ID de nuestro usuario, una cadena criptográficamente fuerte [3] que apunte a un registro en nuestra base de datos. ¿A qué me refiero con esto? A que tampoco grabe directamente el ID de la sesión (dado que siendo sencillamente un número, sería para un atacante trivial probar con diferentes valores hasta "aterrizar" en una sesión interesante), sino una cadena aparentemente aleatoria, creada con un algoritmo que garantice una muy baja posibilidad de colisión y un espacio de búsqueda demasiado grande como para que un atacante lo encuentre a través de la fuerza bruta.

Los algoritmos más comunes para este tipo de uso son los llamados funciones de resumen (digest) [3]. Estos generan una cadena de longitud fija; dependiendo del algoritmo hoy en día van de los 128 a los 512 bits. Las funciones de resumen más comunes hoy en día son las variaciones del algoritmo SHA desarrollado por el NIST y publicado en 1994; usar las bibliotecas que los implementan es verdaderamente trivial. Por ejemplo, usando Perl:

  1. use Digest::SHA1;
  2. print Digest::SHA1->sha1_hex("Esta es mi llave");

nos entrega la cadena:

c3b6603b8f841444bca1740b4ffc585aef7bc5fa

Pero, ¿qué valor usar para enviar como llave? Definitivamente no queremos enviar, por ejemplo, el ID de la sesión - Esto nos pondría en una situación igual de riesgosa que incluir el ID del usuario. Un atacante puede fácilmente crear un diccionario del resultado de aplicar SHA1 a la conversión de los diferentes números en cadenas. La representacíon hexadecimal del SHA1 de '1' siempre será d688d9b3d3ba401b25095389262a3ecd2ad5ad68, y del de 100 siempre será daaaa8121aa28fca0edb4b3e1f7b7c23d6152eed. El identificador de nuestra sesión debe contener elementos que varíen según algún dato no adivinable por el atacante (como la hora exacta del día, con precisión a centésimas de segundo) o, mejor aún, con datos aleatorios.

Este mecanismo nos lleva a asociar una cadena suficientemente aleatoria como para que asumamos que las sesiones de nuestros usuarios no serán fácilmente "secuestradas" (esto es, que un atacante no le atinará al ID de la sesión de otro usuario), permitiéndonos dormir tranquilos sabiendo que el sistema de manejo de sesiones en nuestro sistema es prácticamente inmune al ataque por fuerza bruta.

Como último punto: Recuerden que algunas personas, por consideraciones de privacidad, han elegido desactivar el uso de galletas en su navegación diaria, a excepción de los sitios que expresamente autoricen. Tomen en cuenta que una galleta puede no haber sido guardada en el navegador cliente, y esto desembocará en una experiencia de navegación interrumpida y errática para dichos usuarios. Es importante detectar si, en el momento de establecer una galleta, ésta no fue aceptada, para dar la información pertinente al usuario, para que sepa qué hacer y no se encuentre frente a un sistema inoperativo más.

REFERENCIAS

  • 1. Es aceptable que a través de un GET, por ejemplo, aumente el contador de visitas, esto es, que haya un cambio no substantivo — Pero muchos desarrolladores han sufrido por enlazar a través de un GET (generado por una liga HTML estándar), por ejemplo, el botón para eliminar cierto objeto. Toda acción que genere un cambio en el estado substantivo de nuestra base debe llevarse a cabo a través de POST. ¿Qué pasa en caso contrario? Que diversas aplicaciones, desde los robots indexadores de buscadores como Google y hasta aceleradores de descargas ingenuos que buscan hacer más ágil la navegación de un usuario (siguiendo de modo preventivo todas las ligas GET de nuestro sistema para que el usuario no tenga que esperar en el momento de seleccionar alguna de las acciones en la página) van a disparar éstos eventos de manera inesperada e indiscriminada.
  • 2. En términos de redes, HTTP implementa exclusivamente la capa 4 del modelo OSI, y si bien TCP mantiene varios rasgos que nos permiten hablar tambien de sesiones a nivel conexión, estas son sencillamente descartadas. Las capas 5 y superiores deben ser implementadas a nivel aplicación.
  • 3. La galleta será enviada en cada solicitud que se haga, por lo que se recomienda mantenerla corta. Varias implementaciones no soportan más de 4KB.
( categories: )

acts_as_catalog, real_fk y acts_as_magic_model — Tres plugins simples para toda ocasión

Submitted by gwolf on Fri, 06/05/2009 - 08:29
Written in...: 
2009
I prepared this for a short presentation at RubyCamp UNAM. I present the three Rails plugins I wrote in 2008, acts_as_catalog, real_fk and acts_as_magic_model
Resumen: 
Preparé esto para una presentación corta en el RubyCamp UNAM. Presento los tres plugins de Rails que escribí en 2008: acts_as_catalog, real_fk y acts_as_magic_model

Evitando las Inyecciones de SQL

Submitted by gwolf on Fri, 05/22/2009 - 11:42
Wolf G.  2009.  Evitando las Inyecciones de SQL. Software Gurú. :50-51.

En la edición anterior de SoftwareGurú prometí que en esta columna trataría temas relativos a la seguridad en cómputo, a cómo escribir código más confiable y más robusto.

Las vulnerabilidades más comunes son también las más fáciles de explotar para un atacante - Y utilizando algunas prácticas base, son también las más fáciles de evitar o corregir: Casi todas ellas se originan en la falta de validación (o exceso de confianza) en los datos que nos proporciona el usuario.

Prácticamente la totalidad de los sistemas que desarrollemos procesarán datos provenientes de terceros. Ya sea mostrando o grabando lo expresado en formas HTML, determinando el flujo de nuestra aplicación a través de rutas y parámetros o «galletas» HTTP, o incluso -considerando la tendencia de migración hacia un esquema de «cloud computing»- tomando resultados de procedimientos remotos en sistemas no controlados por nosotros, a cada paso debemos emplear datos en los que no confiamos.

Esta puerta de entrada permite a un atacante una amplia variedad de modalidades de intrusión. En general, podemos hablar de ellas como inyección de código interpretado - Y en esta ocasión hablaremos específicamente de inyección de SQL.

En el desarrollo de sistemas debemos partir siempre del principio de mínima confianza: No debemos confiar en ningún dato proveniente de fuera de nuestro sistema, independientemente de quién sea el usuario. Esto es especialmente importante cuando requerimos que un elemento cruce entre las principales barreras de las diversas capas de nuestro sistema.

Tomemos como primer ejemplo al sistema de gestión de contenido que usa SoftwareGurú. Si quisieran leer la edición anterior de esta columna, a la que hice referencia hace algunas líneas, pueden encontrarla en:

http://www.sg.com.mx/content/view/825

Todos hemos analizado URLs, y resultará obvio que «825» corresponda al ID de la nota en la base de datos, y que los componentes «content» y «view» indiquen la operación que el sistema debe realizar ante una solicitud. Ahora bien, ¿a qué me refiero a que cruzamos las barreras entre las capas? ¿Y cuáles son las principales?

Enfoquémonos en el ID. Al analizar el URL, el ID es un pedazo de texto (formalmente es una cadena que es recibida como parte del método GET, uno de los métodos definidos para el protocolo HTTP). El servidor Web Apache que recibe mi solicitud interpreta este método GET y encuentra -utilizando mod_rewrite, indicado por el archivo htaccess- que el contenido indicado por la ruta /content/view/* debe ser procesado por el archivo index.php, que a su vez es manejado por el lenguaje PHP. El archivo index.php corresponde en este caso al sistema Joomla, que reconoce la ruta, convierte al ID en su representación numérica y lo utiliza para pedir a la base de datos le entregue los datos relacionados con determinado artículo. Entonces, aquí podemos reconocer los siguientes puntos principales de manipulación de la solicitud:

  • Apache recibe una solicitud HTTP, y (via mod_rewrite) la reescribe, indicando «content», «view» y «825» como parámetros a index.php
  • PHP analiza, separa y estructura los parámetros recibidos para ser utilizados por Joomla
  • Joomla solicita el artículo 825 a la base de datos

La variabilidad de los primeros pasos es en realidad menor - Pero al solicitar a la base de datos el artículo «825» (y este es el caso base, el más sencillo de todos) deben pasar muchas cosas. Primero que nada, «825» es una cadena de caracteres. PHP es un lenguaje débilmente tipificado (los números se convierten en cadenas y viceversa automáticamente según sea requerido), pero una base de datos maneja tipos estrictamente. Como atacante, puedo buscar qué pasa si le pido al sistema algo que no se espere - Por ejemplo, «825aaa». En este caso (¡felicidades!), el código PHP que invoca a la base de datos sí verifica que el tipo de datos sea correcto: Hace una conversión a entero, y descarta lo que sobra. Sin embargo (y no doy URLs por razones obvias), en muchas ocasiones esto me llevaría a recibir un mensaje como el siguiente:

Warning: pg_execute() [function.pg-execute]: Query failed: ERROR:
invalid input syntax for integer: "825aaa" in
/home/(...)/index.php on line 192

Esto indica que uno de los parámetros fue pasado sin verificación de PHP al motor de base de datos, y fue éste el que reconoció al error.

Ahora, esto no califica aún como inyección de SQL (dado que el motor de bases de datos supo reaccionar ante esta situación), pero estamos prácticamente a las puertas. Podemos generalizar que cuando un desarrollador no validó la entrada en un punto, habrá muchos otros en que no lo haya hecho. Este error en particular nos indica que el código contiene alguna construcción parecida a la siguiente:

"SELECT * FROM articulo WHERE id = $id_art"

La vulnerabilidad aquí consiste en que el programador no tomó en cuenta que $id_art puede contener cualquier cosa enviada por el usuario - Por el atacante en potencia. ¿Cómo puedo aprovecharme de esto? La imaginación es lo único que me limita.

Presentaré a continuación algunos ejemplos, evitando enfocarme a ningún lenguaje en específico - Lo importante es cómo tratamos al SQL generado.

Para estos ejemplos, cambiemos un poco el caso de uso: En vez de ubicar recursos, hablemos acerca de una de las operaciones más comunes: La identificación de un usuario vía login y contraseña. Supongamos que el mismo sistema del código recién mencionado utiliza la siguiente función para validar a sus usuarios:

$data = $db->fetch("SELECT id FROM usuarios WHERE login = '$login' AND passwd = '$passwd'");
if ($data) { $uid = $data[0];
} else { print "<h1>Usuario inválido!</h1>";
}

Aquí pueden apreciar la práctica muy cómoda y común de interpolar variables dentro de una cadena - Muchos lenguajes permiten construir cadenas donde se expande el contenido de determinadas variables. En caso de que su lenguaje favorito no maneje esta característica, concatenar las sub-cadenas y las variables nos lleva al mismo efecto.

Sin embargo... ¿Qué pasaría aquí si el usuario nos jugara un pequeño truco? Si nos dijera, por ejemplo, que el login es «fulano';--», esto llevaría al sistema a ignorar lo que nos diera por contraseña: Estaríamos ejecutando la siguiente solicitud:

SELECT id FROM usuarios WHERE login = 'fulano';--' AND PASSWD = ''

La clave de este ataque es confundir a la base de datos para aceptar comandos generados por el usuario - El ataque completo se limita a cuatro caracteres: «';--». Al cerrar la comilla e indicar (con el punto y coma) que termina el comando, la base de datos entiende que la solicitud se da por terminada y lo que siga es otro comando. Podríamos enviarle más de un comando consecutivo que concluyera de forma coherente, pero lo más sencillo es utilizar el doble guión indicando que inicia un comentario. De este modo, logramos vulnerar la seguridad del sistema, entrando como un usuario cuyo login conocemos, aún desconociendo su contraseña.

Pero podemos ir más allá - Siguiendo con este ejemplo, típicamente el ID del administrador de un sistema es el más bajo. Imaginen el resultado de los siguientes nombres de usuario falsos:

  • ninguno' OR id = 1;--
  • '; INSERT INTO usuarios (login, passwd) VALUES ('fulano', 'de tal'); --
  • '; DROP TABLE usuarios; --

No puedo dejar de sugerirles visitar al ya famoso «Bobby Tables»1.

¿Y qué podemos hacer? Protegerse de inyección de SQL es sencillo, pero hay que hacerlo en prácticamente todas nuestras consultas, y convertir nuestra manera natural de escribir código en una segura.

La regla de oro es nunca cruzar fronteras incorporando datos no confiables - Y esto no sólo es muy sencillo, sino que muchas veces (específicamente cuando iteramos sobre un conjunto de valores, efectuando la misma consulta para cada uno de ellos) hará los tiempos de respuesta de nuestro sistema sensiblemente mejores. La respuesta es separar preparación y ejecución de las consultas. Al preparar una consulta, nuestro motor de bases de datos la compila y prepara las estructuras necesarias para recibir los parámetros a través de «placeholders», marcadores que serán substituídos por los valores que indiquemos en una solicitud posterior. Volvamos al ejemplo del login/contraseña:

$query = $db->prepare('SELECT id FROM usuarios WHERE login = ? AND PASSWD = ?');
$data = $query->execute($login, $passwd);

Los símbolos de interrogación son enviados como literales a nuestra base de datos, que sabe ya qué le pediremos y prepara los índices para respondernos. Podemos enviar contenido arbitrario como login y password, ya sin preocuparnos de si el motor lo intentará interpretar.

Revisar todas las cadenas que enviamos a nuestra base de datos puede parecer una tarea tediosa, pero ante la facilidad de encontrar y explotar este tipo de vulnerabilidades, bien vale la pena. En las referencias a continuación podrán leer mucho más acerca de la anatomía de las inyecciones SQL, y diversas maneras de explotarlas incluso cuando existe cierto grado de validación.

El tema de la inyección de código da mucho más de qué hablar - Lo abordaremos en la próxima columna, desde otro punto de vista.

Referencias:

  • 1. Bobby Tables - http://www.xkcd.com/327
( categories: )

¿Y por qué cambiar?

Submitted by gwolf on Mon, 04/20/2009 - 17:46
Wolf G.  2008.  ¿Y por qué cambiar? PC Magazine en español.

Tiene más de diez años que dí el brinco y migré a Linux - Ya no sólo como mi entorno primario, sino como el único sistema operativo que tengo instalado en cualquiera de mis computadoras.

En 1997, obviamente, hacer esto no estaba exento de problemas o retos. Yo soy del grupo de usuarios que decidieron migrar en primer término por razones ideológicas, a partir de la convicción de que el Software Libre es superior moralmente a cualquier sistema propietario, como Windows o MacOS.

Claro, ya desde hace diez años -e incluso para los más convencidos- era muy importante el argumento de la calidad y la estabilidad. Linux -heredero de la familia de sistemas operativos Unix y más de 35 años de historia- mantuvo desde sus humildes principios al ser desarrollado en la computadora personal de un estudiante finlandés de posgrado la que posiblemente aún hoy es su principal característica: La modularidad. Una computadora que corre con Linux tiene siempre en ejecución a cientos de pequeños programas, cada uno de ellos altamente especializado y de propósito claramente limitado, pero orientados a la integración - con interfaces públicas claras y bien documentadas. ¿Qué significa esto?

Todos los programas que utilizamos son, obviamente, escritos por seres humanos. Por más talentoso que sea un programador, no queda exento de equivocaciones. Estas son, sin entrar en demasiados detalles, las que llevan a errores en el comportamiento de nuestros programas. La gran ventaja, pues, que tienen los sistemas operativos tipo Unix es que, al ser tan modulares, no sólo nos es muchísimo más fácil localizar qué es lo que causa determinado fallo, sino ayudan a asegurar que, una vez corregido éste, su impacto será el menor posible. Esta es una de las grandes razones por las que el entorno GNU/Linux, incluso en su infancia (en que era desarrollado meramente como hobby, no tenía detrás a todas las empresas que hoy en día lo impulsan), fuera claramente más confiable para aplicaciones de servidor que Windows, que cuenta con la empresa de software más grande del mundo.

Desde 1997, claro, ya hemos avanzado muchísimo: Hoy en día hay gente que comienza a usar programas libres (como los reseñados en esta revista) o se muda por completo a Linux ya no sólo por ideología o por ganas de aprender, sino porque el entorno es más atractivo, estético, funcional o incluso compatible con su forma de trabajo - ¡sí! Hoy en día, programas como OpenOffice o Gimp son compatibles con una gama de formatos mucho más amplia que sus contrapartes propietarias, y sus interfaces de usuario se han vuelto más intuitivas que las últimas versiones de la competencia, sobre-cargada de funcionalidad rara vez utilizada.

Otra ventaja central de Linux es que es Software Libre - Y no, con esto no me refiero a que Linux sea gratuito (aunque típicamente lo sea, hay una industria muy importante construída alrededor del Software Libre). Si bien al día de hoy tengo más de diez años con Linux, tengo más de 25 como usuario frecuente de computadoras.

Me inicié aún con las "minicomputadoras" de principios de los 80, con decenas de terminales, y me tocó vivir y aprender la época de las Apple II y de las Commodore 64, las primeras PC... Y si bien todas ellas brindaban conocimiento y experiencias importantísimas para todo quien, como yo, a la postre se dedicaría al cómputo como actividad profesional, siempre partían de la lógica de que había un núcleo operativo cerrado, casi sagrado, que La Empresa (fuera en este caso Apple, Commodore, IBM o Microsoft) nos daba, sobre del cual podríamos construir. Con el software libre, sin embargo, lo que recibimos es una invitación a asomarnos a cada uno de los componentes del sistema - Una invitación con toda la documentación necesaria, con todas las herramientas de desarrollo y depuración para lograrlo.

La diferencia es tremenda: Un usuario de Linux con algo de curiosidad científica no se enfrenta a la "magia negra" de cómo funciona el sistema, y no acepta el argumento de que "la computadora no quiere" o que "la computadora está lenta". Un usuario de Linux, con el paso del tiempo, va comprendiendo las funciones y relaciones entre los componentes del sistema, y se vuelve capaz de corregir un problema. No sólo eso; a través de todo tipo de foros de intercambio, podemos participar en el desarrollo, mantenimiento o soporte de todos esos programas que tanto nos han dado.

Entonces, en resumen... ¿Por qué me mudé al Software Libre, y por qué sigo ahí? Porque me dio herramientas indispensables para ser un usuario más feliz, más profesional - y una mejor persona, miembro activo de una comunidad que me ha dado todo lo necesario para mi desarrollo profesional.

( categories: )

Para mis documentos, movilidad y administración

Submitted by gwolf on Mon, 04/20/2009 - 17:42
Wolf G.  2007.  Para mis documentos, movilidad y administración. PC Magazine en español.

Hay más de una manera de abordar el tema de la movilidad - Podemos pensar, por un lado, en los dispositivos que nos permiten movernos junto con nuestra información (PDAs, teléfonos celulares cada vez más inteligentes, Tablet PCs, computadoras portátiles cada vez más compactas y potentes - Incluso la ya omnipresente "llave" USB es un dispositivo fundamental para la movilidad diaria), o por otro lado podemos referirnos a las aplicaciones que permiten que nuestros datos se muevan a donde nosotros vayamos, dándonos la posibilidad de asumir la movilidad como un hecho, aún si salimos de casa sin un sólo artículo electrónico - A fin de cuentas, lo que buscamos es no tener que acordarnos de nuestros datos, sino que saber que están resguardados y a nuestra disposición en todo momento que los necesitemos.

En esta ocasión voy a platicarles acerca de una herramienta que cambió mi vida como pocas lo han hecho, y se ha vuelto desde hace ya varios años mi fiel compañera a donde sea que yo viaje: Subversion (http://subversion.tigris.org/).

Antes de conocer a Subversion y herramientas similares, nos acostumbramos a ver a nuestros documentos como pedazos independientes de información. Cada archivo es un universo aparte, y cada vez que lo modificamos, en realidad nuestra computadora está creando un archivo nuevo que simplemente coloca en el lugar del anterior. Lo que es peor, ¿a quién no le ha pasado que sale de casa muy confiado con todos los archivos en la memoria USB... Tan sólo para toparse con que traemos una versión anterior de los archivos a la que nos tomó tantas horas de trabajo? O cuando somos varias personas trabajando en un proyecto grande, ¿cómo podemos trabajar cada cual a sus anchas sin el constante temor de estar pisoteando los cambios que hizo alguien más? Todos estos problemas los resuelven los diversos VCS (Version Control Systems, sistemas de control de versiones) - Y mi VCS favorito es Subversion.

Subversion opera bajo un esquema cliente-servidor - Debemos instalarlo en una computadora fija, que será el punto central, y podemos trabajar con él desde la cantidad que queramos de dispositivos fijos o móviles. Claro, podemos permitir el acceso a cuantos usuarios queramos, ya sea sólo para lectura o incluso con derecho a modificar la información.

Con Subversion, ya no vamos a hablar de nuestros documentos meramente como un conjunto no organizado de archivos - Manejaremos un depósito, que tiene -como un todo- diferentes versiones que nos permiten consultar "fotografías" de nuestro trabajo en el tiempo. Puede incluso llevar ramas y etiquetas. Las diferentes ramas, claro, pueden bifurcarse o unirse cuando lo juzguemos necesario. ¿Qué significan todos estos conceptos?

Depósito
El directorio que contiene a todos los archivos relacionados, así como la información interna de Subversion para manejarlos
Documento
Cada uno de los archivos que existen dentro de nuestro depósito, independientemente del tipo de archivo de que se trate.
Versión
Siempre que hacemos un cambio a nuestro depósito, aumentará el número de versión del depósito completo. Esto nos permite ver, por ejemplo, cómo estaba nuestro proyecto completo en determinada fecha, o qué archivos fueron modificados ese fatídico día en que no nos tomamos nuestra taza matutina de café.
Rama
Muchas veces se nos presenta la disyuntiva de qué dirección tomar en un proyecto - Al crear una rama (o, siguiendo el argot del sistema, al bifurcar un proyecto), con Subversion no sacrificamos a una posible salida por otra. Podemos continuar desarrollando en ambos sentidos, y posteriormente evaluar los resultados para volver a unificar las ramas.
Etiqueta
Cuando llegamos a un punto importante en un proyecto, es importante marcar a nuestro depósito con una etiqueta - De este modo, podemos rápidamente volver a exactamente esa versión sin tener que estarla buscando.

¿Cómo se usa? Es muy sencillo - Lo mejor es que la mayor parte del tiempo simplemente trabajaremos como si no estuviera ahí. Sólo debemos recordar tres reglas: Actualizar nuestra copia local al inicio de toda sesión de trabajo, y "empujar" los cambios al servidor tan pronto estén listos. La manera más simple de hacer esto es por medio del cliente de línea de comando, por medio respectivamente de las órdenes "svn update" y "svn commit" - Pero, claro, no es la única manera.

Un componente muy interesante de los sistemas de control de versiones es el de búsqueda de diferencias - ¿Qué archivos modificamos en nuestro depósito en determinado periodo? Y lo que es más, ¿Qué cambios hicimos en ellos? Subversion nos facilita esta información de un sólo golpe, como podemos apreciar en las imágenes que acompañan al artículo. Claro está, toda herramienta tiene su un nicho para el cual funciona de forma óptima; notarán que para utilizar la funcionalidad de comparación entre versiones, los archivos que maneje Subversion deben ser basados en texto - Nuestras páginas Web escritas en HTML, documentos XML, los programas que escribamos en nuestro lenguaje favorito - Obviamente, Subversion no está diseñado para mostrarnos las diferencias entre dos imágenes o entre dos hojas de cálculo.

Uno de los puntos más importantes de su desarrollo es que, más allá de ser sencillamente una aplicación completa y cerrada, Subversion sigue claramente las directrices de diseño en el software libre: Está diseñado como un conjunto de bibliotecas, pensado para ser embebido en programas que puedan aprovechar su potencial y presentarlo de una forma unificada y adecuada para sus usuarios - Hay muy diferentes programas cliente que, utilizando el mismo código (y, por tanto, sin tenernos que preocupar acerca de su interoperabilidad) nos permiten tener acceso a toda la funcionalidad de Subversion.

El primer cliente con el que nos encontraremos, y el más conveniente para consultas cortas y rápidas, es el de línea de comando. Nos permite hacer rápidamente actualizaciones, envíos y comparaciones simples entre versiones - Sí, tendrás que aprenderte unos cuantos comanditos, pero verás que es, en muchas ocasiones, la manera más fácil y rápida de obtener información.

El poderoso y veterano entorno de desarrollo Emacs incluye un modo de trabajo que nos permite manejar la mayor parte de las funciones de Subversion. Podemos ver en la captura de pantalla una sesión típica.

Emacs no es el único entorno de desarrollo que ofrece integración con Subversion. Eric (http://www.die-offenbachs.de/detlev/eric.html), el IDE de Python, ofrece una interfaz muy limpia, pulida y amigable. Podemos también instalar Subclipse (http://subclipse.tigris.org/) para integrarlo a Eclipse, un robusto y completo IDE libre orientado a Java.

Pero incluso si lo tuyo es trabajar con herramientas más tradicionales, que no incluyan aún soporte para Subversion, hay clientes sencillos y completos que se encargarán de mantener nuestros archivos en el depósito. Uno de los más cómodos en Linux es eSvn (http://esvn.umputun.com/). Desde MacOS, svnX (http://www.lachoseinteractive.net/en/community/subversion/svnx/features/) es el que mejor integración nos ofrece con el entorno de Panther.

En Windows, la alternativa más cómoda y completa es TortoiseSVN (http://tortoisesvn.net/), una extensión al explorador del sistema que nos permite administrar nuestros depósitos con toda la naturalidad con que normalmente trabajamos. Además de esto, TortoiseSVN nos proporciona cómodas herramientas para entender de una sóla mirada cómo ha ido avanzando nuestro proyecto, con sus ramas y etiquetas, o las estadísticas de actividad de cada uno de los participantes. Una de las herramientas hermanas de TortoiseSVN nos permite dar, además de todo esto, un importante paso adelante - Con TortoiseDiff (http://tortoisesvn.tigris.org/TortoiseIDiff.html) podemos ver las diferencias de manera intuitiva, incluso entre archivos de contenido binario como las imágenes.

¿Suena ya demasiado bueno para ser cierto? ¡Pero si eso no es todo! Podemos también interactuar con Subversion utilizando simplemente un navegador Web - Y en esta categoría me limitaré a mencionar a algunos de los proyectos más conocidos, como WebSvn (http://websvn.tigris.org/), Insurrection (http://insurrection.tigris.org/) o Chora
(http://www.horde.org/chora/); su uso es bastante similar, y elegirás uno u otro dependiendo de las otras herramientas que emplees para trabajar en tu proyecto.

Subversion implementa un esquema de trabajo tremendamente potente, y al poco tiempo de que comiences a usarlo, te costará trabajo voltear atrás y entender cómo viviste sin él hasta ahora. Es, además, un proyecto excelentemente documentado - Tenemos a nuestra disposición hasta libros completos disponibles en línea detallando su diseño y funcionamiento. Si te interesa estudiarlo a fondo, te recomiendo el libro oficial, "Version Control with Subversion", que está disponible libremente en http://svnbook.red-bean.com/

Imagenes

  • desde_consola.png: Desde consola podemos realizar las operaciones más comunes de manera rápida y sencilla, con sólo aprender un par de comandos
  • emacs.png: El poderoso entorno Emacs incluye un modo para el manejo de proyectos en Subversion
  • eric_diff.png: Mostrando las diferencias entre dos versiones de un depósito, desde Eric, un IDE libre para el desarrollo en Python
  • esvn_diff.png: Mostrando las diferencias entre dos versiones de un depósito, desde eSVN, un cliente de Subversion gráfico y ligero
  • tortoisesvn_arbol.png: TortoiseSVN nos proporciona excelente integración de SVN dentro de un entorno Windows
  • chora.png: Viendo los cambios en uno de nuestros archivos desde Chora, un cliente Web de Subversion
  • insurrection.png: Revisando la bitácora de cambios desde Insurrection, un cliente Web de Subversion
( categories: )

LyX: Para documentos

Submitted by gwolf on Mon, 04/20/2009 - 17:40
Wolf G.  2007.  LyX: Para documentos. PC Magazine en español.

Hay muchas áreas de las interfaces comunes con las que la mayor parte de los usuarios de computadoras trabaja a diario que damos por buenas simplemente por ser las más comunmente utilizadas, sin que necesariamente sean óptimas - y los procesadores de textos, una de las herramientas que más decididamente catapultaron la revolución de las computadoras personales.

¿Qué? ¿Este artículo pone en jaque la superiordad de una de las herramientas más comunes y más exitosas del mundo? En efecto. Y no me voy a limitar a recomendar una alternativa libre al procesador de textos más difundido del mundo Windows, Microsoft Word - A estas alturas, los lectores asiduos de la columna Linux de PC Magazine deben estar más que familiarizados con OpenOffice, Abiword o KWord. Seamos ambiciosos - Como siempre, en la Sección Linux presentaremos una alternativa a la manera tradicional de hacer las cosas. Hoy hablaremos acerca de LyX (http://www.lyx.org/), el procesador de documentos.

Comencemos por el principio: ¿Qué es LyX? ¿Qué lo diferencía de un procesador de textos? ¿Sobre qué base tecnológica está creado?

A mediados de los 70, el computólogo Donald Knuth descubrió, frustrado, que con la tecnología existente era imposible publicar con la calidad que él esperaba su ahora clásica serie de libros, "The Art of Computer Programming". En 1977, arrancó un proyecto que estimó que le llevaría parte de su año sabático en la universidad de Stanford: El lenguaje tipográfico TeX.

El proyecto no fue tan rápido ni sencillo como originalmente lo había previsto - El desarrollo duró más de diez años y se extendió con la ayuda de expertos en diversas áreas de la edición y tipografía de todo el mundo, terminó cubriendo muchas áreas a las que no preveía llegar originalmente, desde un lenguaje para la definición matemática de fonts (MetaFont) hasta un poderoso lenguaje de macros (LaTeX). Y si bien desde 1990 no ha habido cambios fundamentales al sistema TeX, ese no es más que el punto de partida de nuestro tema de este mes.

LaTeX es un sistema muy poderoso, apto para escribir desde simples notas, pero especialmente orientado a documentos complejos y largos, sobre los cuales se vuelve necesario aplicar un estilo consistente de principio a fin, y tal vez separarlos en diferentes archivos, como en el caso de libros donde diferentes autores escriben diferentes artículos. Sin embargo, escribir directamente en LaTeX comunmente espanta a los novatos, pues la definición de los elementos de un documento se debe indicar por medio de un lenguaje, como lo pueden observar en la primer captura de pantalla [IMAGEN: emacs_latex.png], y no es posible ver los resultados de inmediato, sino que tenemos que compilar nuestros textos fuente para poder ver el documento terminado. El lenguaje es simple de aprender (personalmente, lo aprendí siendo aún un niño en 1983), pero no tiene el grado de usabilidad al que estarás acostumbrados hoy en día. Un documento de LaTeX tiene un preámbulo que, si bien puede ser reducido, requiere que le declares -a través de comandos textuales- el tipo de documento que quieres escribir, y sólo no nos permite conocer cómo será el documento una vez terminado, sino que no puedes saber si tecleaste un comando equivocado sino hasta el momento de preparar una versión del documento terminado (de compilarlo). No es terrible, pero fácilmente cansa incluso a los usuarios novatos.

Hacia 1995, Matthias Ettrich inició un proyecto que proporciona a los usuarios la flexibilidad y potencia de LaTeX, y la sencillez de uso de un procesador de textos, LyX. Probablemente conozcas el nombre de Matthias gracias al otro importante proyecto que él inició: El entorno de escritorio KDE. LyX es básicamente una interfaz gráfica que nos permite trabajar con él como lo haríamos con un procesador de textos, y genera el código LaTeX adecuado para nuestros documentos.

Bueno, pero... ¿Cómo puede ser un sencillo programa como LyX superior a los procesadores de textos WYSIWYG ("What You See Is What You Get" - Lo que ves en pantalla es exactamente igual al resultado que obtendrás en papel)? ¿Cómo puede LyX retar a la interfaz de usuario que ha demostrado ser imprescindible en toda computadora? La respuesta inicia con que LyX parte de una filosofía diferente, a la que llama WYSIWYM ("What you see is what you mean" - Lo que ves en pantalla es lo que quieres que LyX y LaTeX hagan por tí). [IMAGENES: wysiwym.png y wysiwym-2.png] Como pueden apreciar en las capturas de pantalla, mientras escribes nuestro documento no tienes que preocuparnos por cosas triviales (cómo queda el acomodo de párrafos por página, si quieres aumentar el interlineaedo o tamaño de letra, si las secciones del documento van de determinado tamaño o en negritas, etc.) Basta con que nos concentrarte en generar el contenido del documento; todo lo relativo a cómo se va a ver cada uno de los componentes del mismo se lo puedes confiar a LaTeX.

Los actuales procesadores de textos evolucionaron para cubrir las necesidades del trabajo en las oficinas - podemos verlo claramente tan sólo porque todos ellos forman parte de una "suite de oficina" como OpenOffice, Microsoft Office, Gnome Office o KOffice. LaTeX, por el contrario, evolucionó de las necesidades de tipógrafos profesionales, y los programas resultantes nos lo hacen ver claramente: Aún cuando hace años ya es poco común ver las antiguas máquinas de escribir en la mayor parte de las oficinas, los procesadores de textos siguen trabajando con ese modelo en mente, mostrándonos una página sobre la cual podemos escribir directamente, y el estilo que elegimos para cada elemento del texto nos es retroalimentado visualmente de inmediato.

Cabe aquí anotar que, desde hace años ya, los principales procesadores de texto nos ofrecen también un marcado semántico de los párrafos (esto es, indicar la función de cada elemento, más que su apariencia), buscando llegar a la eficiencia que LyX nos brinda; pueden ver en las capturas de pantalla de AbiWord y OpenOffice [IMAGENES: esitlo_abiword.png y estilo_openoffice.png] dónde están ubicados tradicionalmente esos elementos. Sin embargo, aún teniendo esta herramienta a la mano, los usuarios están acostumbrados a la "interfaz máquina de escribir", a marcar un encabezado de sección como "18 puntos, negritas, Times New Roman"

Un procesador de documentos como LyX parte, en cambio, del tipo de trabajo que realizan los tipógrafos, en la cual partimos de que quien escribe el documento es el experto en el tema - pero no en la apariencia visual. Esto va mucho más allá de dictar que el usuario no debe elegir el tamaño o tipo de letra a utilizar para un título de artículo o de sección, sino que en detalles mucho más sutiles, que muchos de nosotros de otro modo ignoraríamos - Detalles tipográficos como el manejo de ligaturas, palabras o líneas viudas y huérfanas, tipos de comillas a utilizar, e incluso los diferentes tipos de espacio en blanco a dejar después de diferentes signos de puntuación. En pocas palabras, LaTeX se encarga de que nuestros documentos brillen por su tipografía. [IMAGEN: ligaturas.png]

LaTeX, y por tanto LyX, está orientado principalmente a trabajar con documentos largos. Está especialmente enfocado a facilitar el manejo de la estructura - Partes, capítulos, secciones y subsecciones; listas numeradas, listas simples, descripciones; etiquetas y referencias cruzadas, manejo de bibliografía, notas al pie; tablas de contenido generales, de figuras, índices de conceptos; y muy especialmente los elementos propios de libros y artículos científicos, como fórmulas, definiciones, citas, figuras, tablas, y un larguísimo etcétera. ¿Qué tienen todos estos elementos en común? Todos ellos son elementos estructurales de un documento. Claro, si requieres hacerlo, es posible especificar elementos de diseño directamente (por ejemplo, cambiar el tipo o tamaño de letra), pero la misma lógica general de uso del programa sugiere al usuario no boicotear a su propio trabajo haciendo esto.

Claro está, LyX no es el programa adecuado para todo tipo de documentos - Por ejemplo, si quieres diseñar un cartel o una invitación, seguramente querrás tener control completo del diseño de la página. El espacio en el cual LyX realmente brilla es en la preparación de documentos de varias páginas, con una estructura. ¡Y claro, cómo olvidarlo! con tablas de contenido, índices bibliográficos y demás elementos que, si bien en otros sistemas requieren de una fuerte cantidad de trabajo y mantenimiento, en LyX literalmente se vuelven cuestión de tres clicks.

Ahora bien, ¿cómo compartir el documento con terceros? Hoy en día sonaría anacrónico sugerirte que imprimas tu documento y se lo envíes físicamente a su destinatario, y obviamente, la mayor parte de los usuarios no cuentan con una instalación de LyX. ¿Qué hacer? Muy simple: Del mismo modo que LyX procesa un documento para imprimirlo, puede guardar ese documento en muy diversos formatos. Como primer ejemplo, seleccionando Archivo -> Exportar -> PDF, LyX nos genera un archivo PDF apto para que lo compartamos. Por otro lado, si quieres subir el documento a tu página Web, selecciona Archivo -> Exportar -> HTML, y se generará un árbol de páginas siguiendo la estructura de tu documento (no, no será una sóla página interminable, como resulta con otros productos).

En resumen: LyX es un sistema completamente diferente, que puede ayudarte a generar documentos con un grado de profesionalidad que poca gente puede lograr en un procesador de textos. He visto varios libros (incluso libros dedicados al arte de la correcta tipografía) que son impresos sin modificación alguna sobre de los estilos base de LaTeX. En este artículo me falta espacio para abundar en las virtudes de este programa, pero espero que con los puntos que les he mencionado sea suficiente para picarte la curiosidad acerca de una nueva manera de hacer el trabajo de todos los días.

Imagenes

  • emacs_latex.png: La edición directa de un documento en LaTeX (arriba) y el documento generado (abajo)
  • lyx_inicio.png: LyX nos da la bienvenida
  • wysiwym.png y wysiwym-2.png: Respectivamente, preparación y resultado final de un documento. Estas dos imagenes nos ilustran claramente qué significa WYSIWYM: Al no preocuparnos directamente por cómo queda el resultado final, escribir el documento no nos significa tanta distracción: Podemos concentrarnos en el contenido.
  • estilo_abiword.png y estilo_openoffice.png: Los procesadores de textos ahora presentan también la opción de edición por categorías semánticas y estilos - Sin embargo, al también presentar las tradicionales barras de tamaño, tipo y atributos de letra, la mayor parte de los usuarios no aprenden siquiera las bondades del marcado semántico.
  • ligaturas.png: Mostramos la edición en LyX en la parte superior, y el documento generado en la inferior. Como ejemplo de las sutilezas tipográficas que maneja LaTeX, puedes apreciar cómo varias letras se ligan en un sólo elemento al aparecer después de la 'f' - y cómo podemos "romper" estas ligaturas si así lo requerimos intercalándoles un elemento nulo
( categories: )
Syndicate content