Migrando a la plataforma .NET

En junio de 2000 la firma de Redmond presentó un entorno independiente del lenguaje concebido para facilitar el desarrollo de aplicaciones para Windows capaces de trabajar y comunicarse de forma segura y sencilla. Ocho años después el 70% de los desarrolladores españoles utiliza .NET

Migrando a la plataforma .NET

10 mayo 2008

Las cifras no engañan. En la actualidad la presencia de este entorno de programación de Microsoft en el mercado de las herramientas de desarrollo de aplicaciones para Windows es abrumadora. Y es que esta plataforma se ha consolidado como una alternativa eficaz a J2EE (Java 2 Platform Enterprise Edition) de Sun (http://www.sun.com/java) que, además, proporciona importantes ventajas a la hora de diseñar aplicaciones distribuidas. ¿La clave? Sus dos pilares esenciales: las herramientas y los servicios web.
En las entrañas de .NET
Los componentes que conforman este entorno de desarrollo se denominan .NET Framework y consisten en el CLR (Common Language Runtime) o lenguaje común en tiempo de ejecución, una jerarquía de librerías de clases y soporte para diferentes tipos de aplicaciones, entre las que contempla herramientas modeladas con la arquitectura cliente-servidor tradicional, así como aplicaciones y servicios web. Además, .NET proporciona librerías que facilitan el acceso a bases de datos y código de gestión de XML, no en vano este metalenguaje se ha posicionado en esta plataforma como un estándar de facto para la interoperabilidad de aplicaciones.
El CLR se encuentra en el corazón del entorno, de hecho es una máquina virtual encargada de proporcionar una capa de abstracción entre el bytecode de .NET y la plataforma hardware sobre la que se ejecuta (por ejemplo, la arquitectura x86 y compatibles). Esta capa, equivalente a la máquina virtual de Java en el entorno J2EE, tiene implicaciones serias en lo que al desarrollo y la seguridad de las aplicaciones se refiere. En esencia, el CLR evita que el desarrollador acceda a bajo nivel a la plataforma, siendo este componente el único que puede, por ejemplo, acceder a la memoria y gestionar directamente los punteros. Además, el código desarrollado para la plataforma .NET se ejecuta bajo ciertos roles de seguridad configurables que varían si es de confianza (trusted) y también en función de su procedencia (la intranet, Internet, etc.).
Muchos de los ataques que han sufrido los sistemas operativos Windows han comenzado a desaparecer (entre ellos, el consabido e infame desbordamiento de buffer) debido a que ahora el programador no tiene acceso a la gestión nativa de la memoria de manera directa. Es evidente que tener esta capacidad, esta capa extra que abstrae de la manipulación directa del hardware (igual que ocurre en el entorno Java), tiene su coste en el tiempo de ejecución, máxime si las aplicaciones que escribimos son distribuidas. Cualquier compilador que genere código para la plataforma .NET debe hacerlo en un lenguaje intermedio conocido como IL (Intermediate Language), que a su vez se ajusta a una especificación que aglutina las pautas que deben cumplir los lenguajes .NET (CLS o Common Language Specification). El IL obtenido se traducirá a código nativo usando estrategias just-in-time, por lo que la sobrecarga de tiempo sufrido por esta capa intermedia no resulta excesiva.
Una plataforma plurilingüe
Como mencionamos antes, el Framework .NET es independiente del lenguaje utilizado, lo que significa que los que escojamos para nuestras aplicaciones deben ser traducidos a un lenguaje binario comprensible por la plataforma. Por esta razón, los desarrolladores que utilizan los servicios de esta plataforma pueden emplear varios lenguajes de alto nivel y hacerlos operar entre sí. Microsoft ha creado compiladores para Visual Basic, C++ y C#, aunque también existen compiladores para esta plataforma que no han sido creados por Microsoft.
La neutralidad del lenguaje ha incrementado mucho el atractivo de la tecnología .NET, en especial cuando se considera el coste que es necesario afrontar para migrar desde otras plataformas. De hecho, la migración desde J2EE es prácticamente inmediata, ya que una aplicación Java puede convertirse sin mucho esfuerzo en un programa .NET utilizando compiladores cruzados. Es más, cualquier grupo de desarrolladores Java puede adoptar C# como lenguaje sin graves consecuencias.
C# es el lenguaje abanderado por Microsoft como estandarte para los programadores .NET y, a la par, la herramienta con la que persigue la captación de desarrolladores de otras plataformas. No en vano, para atraer programadores procedentes del entorno Java, Microsoft creó una iniciativa denominada JUMP (Java Users Migration Path), que significa algo así como «el camino que debe seguir un desarrollador Java para programar en .NET». En cualquier caso, Java y C# son dos lenguajes similares, sobre todo a nivel semántico, una capacidad que permite la existencia de aplicaciones que facilitan la conversión entre ambos lenguajes.
¿Es más seguro .NET que J2EE/Java?
La plataforma .NET es, sin lugar a dudas, un marco de ejecución mucho más seguro que el entorno estándar que proporcionaba Windows. Esto no quiere decir que no exista la posibilidad de que se den agujeros de seguridad; de hecho, hay muchos problemas de esta índole a los que se deben enfrentar los desarrolladores. Aun así, es justo decir que carece de muchos fallos de seguridad que Java sí sufrió. Es más, es evidente que Microsoft ha aprendido de los errores que cometió Sun en sus inicios, aunque esto también puede ser contraproducente porque no ha iniciado su camino desde cero, y esa falta de experiencia puede ser también motivo de desconfianza.
Por muy cuidado que esté el desarrollo de la plataforma .NET, y, de hecho, lo está, se puede llegar a la conclusión de que realmente el responsable de las potenciales situaciones de peligro es el propio desarrollador, que en muchas ocasiones ignora los principios básicos de la programación segura. Aquellos programadores que crean aplicaciones web poco seguras con VBScript y ASP a buen seguro escribirán aplicaciones potencialmente inseguras cuando desarrollen con Visual Basic en ASP.NET (un componente del Framework .NET que hace posible la existencia de aplicaciones y servicios web). El CLR no proporciona protección contra el código que contiene defectos o que expone de manera innecesaria información privada o comprometedora. Las librerías de clases de .NET están dotadas de numerosas características de seguridad que se emplean para garantizar que las aplicaciones serán lo más seguras y robustas posible.
En el ámbito de las utilidades web un ejemplo de estas capacidades es la posibilidad de codificar en HTML todos los datos para evitar los ataques XSS o cross-site scripting (que aprovechan las vulnerabilidades del sistema de validación del HTML empotrado), y proporcionar métodos simples pero seguros a la hora de construir consultas SQL dinámicas para evitar ataques por inyección de SQL. Además, el control y la gestión de las sesiones en ASP.NET se han hecho omnipresentes. De esta forma, además del control proporcionado, también se permite el escalado o crecimiento de aplicaciones web desde un servidor a un conjunto de ellos, almacenando las variables de sesión en máquinas de bases de datos SQL-Server.
Afrontando la migración
Cuando se transporta una aplicación de considerables dimensiones de una plataforma a otra, a buen seguro habrá problemas con el código. En algunos casos el código con el que han sido desarrollados los componentes usados por la aplicación a migrar no está disponible o incluso el hardware al que pretendemos migrar impondrá sus propias condiciones, haciendo que en numerosos casos no sea posible. Las aplicaciones que usan código de bajo nivel para comunicarse con otros componentes de la lógica no pueden ser portadas al entorno gestionado o managed que ofrece el CLR. Por esta razón, Microsoft ha incluido cierta funcionalidad en este último componente que le permite, en determinadas circunstancias, ejecutar código no gestionado (unmanaged) precedido por la sentencia unsafe. Esta palabra clave es muy interesante desde el punto de vista de la seguridad.
Supongamos que una aplicación web gestiona la petición y entrada de datos de los usuarios con C# (código gestionado). Tras ser procesada, esta información es transmitida a un código no gestionado que accede a la memoria compartida de un componente desarrollado por terceros. Este elemento se encarga de la comunicación con el servidor de la empresa. Bajo este escenario el código que se ejecuta en la sección unmanaged puede contener errores, por lo que un usuario del sistema podría dar al traste con toda la aplicación e, incluso, ejecutar código arbitrario. Por esta razón, la palabra clave unsafe debe ser evitada en la medida de lo posible, pues cancela todas las opciones de seguridad que la plataforma y el CLR ofrecen a las aplicaciones.
El Framework .NET también se ve perjudicado por la tentación de empaquetar aplicaciones completas desarrolladas en C++ con esta palabra clave y proclamar que han sido portadas, abriendo enormes agujeros de seguridad injustamente atribuidos a .NET. Esta técnica es popular entre los desarrolladores que andan justos de tiempo para cumplir los plazos impuestos por los clientes.
Previniendo ataques
Cuando se desarrolla código que ha de ejecutarse en el cliente es importante recordar que el usuario puede manipular e interrumpir la ejecución independientemente del lenguaje en que se haya escrito (Java, un lenguaje .NET, C++, C, etc.). Con paciencia y tiempo el código puede ser sometido a técnicas de ingeniería inversa que muestran detalles relevantes, por lo que se recomienda no obviar los consejos de codificación segura y adoptar criterios sensatos, como no incluir en los ejecutables información sensible, en especial contraseñas.
Tanto los bytecodes de Java como los ensamblados de .NET pueden ser fácilmente sometidos a técnicas de este tipo, incluso de manera más sencilla que los ejecutables de una plataforma nativa. Esto se debe a que tanto .NET como Java poseen una capa abstracta intermedia que se apoya en una especificación sencilla y limpia, por lo que «entender» los ejecutables para estas plataformas (existen herramientas como dotfuscator para dificultar estas prácticas) es más sencillo que «descompilar» los ejecutables nativos (que se ejecutarán directamente en los procesadores).
Desde la perspectiva de la seguridad del cliente, un usuario puede establecer reglas que otorguen diferentes privilegios a distintas porciones del código. El que se ejecuta en un entorno restringido y controlado se dice que se procesa en un sandbox o cajón de arena. Un usuario puede elegir ejecutar automáticamente código que ha sido firmado digitalmente por ciertas compañías y, de esta forma, otorgar todos los privilegios sobre su máquina a cualquier código que provenga de la intranet de esas firmas.
De este modo, toda aplicación que provenga de un entorno no identificado será etiquetada como código hostil y su ejecución se llevará a cabo en un entorno restringido y muy controlado. No obstante, se han documentado muchos ataques cuyo origen se atribuye a errores de implementación en los sandbox de varios distribuidores de máquinas virtuales de Java que suelen estar íntimamente relacionados con los cargadores de clases. Y es que si se puede ocultar o falsear la identidad del cargador que proporciona el código a ejecutar en el cliente, entonces es posible permitir que el código de un proveedor no autorizado pueda hacerse pasar por código de un proveedor de confianza, logrando así el acceso a todos los privilegios en el sistema del usuario. Por ahora no se han dado este tipo de ataques en la plataforma .NET.
Un crecimiento imparable
El entorno de desarrollo de Microsoft proporciona muchas mejoras en la seguridad y facilita sensiblemente la implementación de aplicaciones distribuidas. A pesar de que la plataforma de desarrollo J2EE de Sun es bastante más madura que el Framework .NET, los de Redmond están apostando fuerte y se están haciendo con apoyos clave de terceros, muy importantes en el sector. Las ventajas de ejecutar código gestionado son innegables y el CLR ha aprendido de los ataques sufridos por Java, evitando de esta forma una gran parte de las debilidades conocidas hasta la fecha. Sin embargo, es importante recordar que los defectos y errores en la implementación de cada desarrollador son indetectables, y quizás los atacantes estén más interesados en explotar estas debilidades para acceder a los datos de la empresa que en lograr acceso al servidor en el que se está ejecutando la aplicación.
SOAP y los servicios web
Una característica clave de la plataforma .NET es el soporte de XML como medio para la interacción entre aplicaciones y servicios web. Estos últimos exponen fragmentos de la lógica de negocio de la aplicación fuera de los cortafuegos, aunque normalmente hacen uso de SOAP (Simple Object Access Protocol), un protocolo que facilita el intercambio de datos codificados en XML y la realización de llamadas a procedimientos remotos (invocación de funciones o métodos de otras aplicaciones, ya sean locales o remotas).
El entorno .NET proporciona librerías que aseguran el transporte de los datos a través de un protocolo (como por ejemplo HTTP) y su seguridad empleando certificados digitales y facilitando así la creación de servicios web. El CLR, por su parte, garantiza la robustez de la aplicación que procesa los datos recibidos a través de un servicio web. No obstante, utilizar SOAP para comunicar un componente .NET con otro J2EE no garantiza que este proceso se lleve a cabo sin dificultades. La organización para la Interoperabilidad de Servicios Web (WSI) trabaja para resolver estos inconvenientes, un problema sobre el que es necesario reflexionar a la hora de portar un servicio web a .NET.