En recientes días escribí un post sobre cómo hacer nuestra navegación más segura usando Opera (en inglés). El principio de operación es simple: deshabilitar JavaScript por defecto excepto para aquellos sitio en los que plenamente confío.
No fue capricho. En alguna ocasión mordí un anzuelo y, sin intención, mi PC participó en un ataque masivo contra Freenode. ¿Cómo ocurrió? Visité una página que tenía un código malicioso de JavaScript, el cual se ejecutó automáticamente (tal vez gracias a onMouseOver o a onLoad) y mandó spam a dicha red. Me bloquearon varias horas y tuve que contactar a los administradores para explicar el caso. Ellos lidian con este tipo de cosas todo el tiempo (¡señal!).
No es el único tipo de problema de seguridad de JavaScript. Tampoco es el único ataque facilitado por JavaScript: como referencia les dejo XSS, X/CSRF, Clickjacking, Revestimiento de interfaz (UI redressing), fuga de datos personales, etc. No que requieran de este lenguaje, sino que con él son mucho más fáciles de implementar sin que el navegador pueda detectarlos.
Busqué formas de evitar llegar a esa conclusión; después de todo, la mayoría de los sitios Web usan JavaScript. ¿Es posible que el navegador sólo ejecute código bien intencionado? No. Una computadora es tonta. No puede distinguir código malicioso. Incluso existe una broma sobre el tema. ¿Cómo le hacen los antivirus? Se basan en firmas y bases de datos alimentadas por humanos y aún así a veces arrojan falsos positivos. Peor aún, también arrojan falsos negativos.
Sin embargo yo necesito una forma de confiar en mi PC. En el momento en que pierde mi confianza, no sé si está interceptando lo que tecleo o lo que transmito, o si las páginas que me muestra son ilegítimas, o si los programas está alterados de otro modo. No sé si mis passwords y datos de tarjetas de crédito están siendo transmitidos por la red a algún cibercriminal. Y no, no estoy exagerando. Desde el momento en que pierde mi confianza tengo que aplicarle un análisis forense, aunque, por lo general, sale más rápido reinstalar.
Lo mismo ocurre con el navegador. Yo confío en que el navegador me permite mantener el control mientras navego. Si no confío en el navegador no puedo confiar en los datos que se envían o se reciben, en el contenido de los sitios que visito o si realmente estoy yendo a los sitios que creo. Si mi navegador está comprometido ni siquiera puedo confiar en el cifrado SSL, por lo que ni con HTTPS puedo asegurar la confidencialidad o integridad de mis transacciones.
No por nada existe NoScript para FireFox.
Analogía con otro error histórico: los privilegios de administrador
No porque sea la opción más popular significa que sea la mejor o la más segura. Me recuerda a los llamados derechos de administrador. Cuando comencé a usar a Linux, en tiempos de Windows 2000 y cuando 98 era todavía lo más popular, los linuxeros me decían que nunca usara mi PC como root («root» el usuario privilegiado en Linux, similar al «Administrator»). Yo pensaba: pero entonces no puedo instalar programas, no puedo configurar hardware, mi CD-RW no puede pedir todo el CPU y es propenso a fallas de escritura, no puedo ‘tunear’ mi sistema igual, etc. Todos esos «problemas» eran precisamente los motivos para no usar root por defecto. ¡Es bueno que no tan fácilmente se instale un programa o hardware! ¡Es bueno que no cualquier programa se adueñe del CPU! ¡Es bueno que no tan fácil se puedan hacer ajustes al sistema!
No porque sea la opción más popular significa que sea la mejor o la más segura. Windows 9x era lo más popular en ese entonces y 2000, intentando traerse el núcleo de NT, ahora multiusuario, apenas comenzaba a tomar auge. Muchos programas se rompieron por ese cambio de paradigma.
Ese pequeño cambio de pensar tenía muchísimas ventajas. En Linux y en Mac es rarísimo ver un virus. No que no los haya, pero no son tan fáciles de hacer y tenemos más líneas de defensa. Cada usuario tiene su espacio bien segmentado. Un usuario no puede ver el contenido de otro usuario. Un usuario no puede dañar el sistema, sólo su propia cuenta. Si un virus se inyecta en el sistema es porque explotó una vulnerabilidad y se reconoce como tal.
Pero seguía existiendo la necesidad de instalar programas, y no era fácil. Había que resolver el problema. Aquí es donde tenemos que hacer una pausa, quedarnos en «still» y analizar las posibles rutas:
- Considerar que el problema es la falta de privilegios por default y cambiarse a usar root por default, como lo hacía Windows 2000. Windows 9x ni siquiera tenía el concepto de privilegios; todos eran «administradores».
- Considerar que el problema es el tedio, y crear métodos seguros para hacer fácil la escalación controlada de privilegios, pero manteniendo un nivel aceptable de seguridad. Habría que inventarlo de ser necesario.
Afortunadamente en Linux cuenta con su y sudo. Igual, para Windows XP SP2, Microsoft mejoró significativamente la delimitación de las facultades de los usuarios restringidos. Si en Windows 2000 muchos programas se rompieron, con este cambio hubo otra barrida; era obvio. Muchos programadores se quejaron, pero era necesario. En Windows Vista incluyeron UAC pero el plan estuvo mal ejecutado. Me han contado que en Windows 7 finalmente dieron en el clavo. Se tardaron más de 10 años, pero así se hace un cambio de esta magnitud.
Yo veo el uso de JavaScript como estar en la primera opción, atacando el problema incorrecto, y hay que pasarse a la segunda opción. Con la popularidad actual de JavaScript, el cambio tendría que ser sumamente doloroso, pero igualmente necesario.
De vuelta al tema
La única manera de estar seguros contra ataques de JavaScript es apagándolo. Si no lo hago así, cualquier persona puede ejecutar código en mi navegador con simplemente incluirlo en un sitio que yo visite. El código puede ser confiable o malicioso, y en cualquiera de los casos se ejecuta sin mi consentimiento, es decir, antes de yo validar o aceptar dicha ejecución. La única forma de prevenirlo es que yo lo habilite sólo para los dominios en los que confío.
¿Basta con deshabilitar JavaScript? No. Hay otros modos de atacar, pero JavaScript los facilita tremendamente; la mayoría de los ataques están basados y ofuscados con JavaScript.
¿Qué fue lo que salió mal?
¿Cómo es que pasamos del deseo de ser productivos, a tener corriendo en nuestras computadoras un lenguaje que permite rastrearnos, atacar otros sitios, etc.?
La escalera de la programación
Peldaño 0. Todo comienza por el principio, y en este caso, las computadoras son simples máquinas que harán lo que su operador le diga. A fin de cuentas eso son: viles, patéticas y despreciables herramientas que simplemente siguen las órdenes de un humano. El programa se hace en un lenguaje cualquiera.
Peldaño 1. No todos los humanos tienen la capacidad de hablarle a una PC en su idioma. Las instrucciones son demasiado primitivas y básicas, de modo que se vuelve una mezcla de ciencia y arte. Se requiere de tiempo y dedicación para hacer un programa medianamente complejo. Entonces, otras personas hacen programas por nosotros. Nosotros nos limitamos a instalarlos en nuestra PC. La PC sigue haciendo lo que el operador le dice… o no exactamente: hará lo que le diga el programador por medio de la autorización del operador.
Existe ahora un punto débil: el operador ahora debe confiar en el programador, pues las instrucciones pueden ser maliciosas. ¿Qué tal si el código del programador, como parte del programa, dice que tome archivos y los mande por correo o por fax? La computadora no puede distinguir la intención de esa instrucción. El código puede ser sintáctica y semánticamente válido y aún así realizar funciones maliciosas. Sólo queda, como línea de defensa, que el operador tome la decisión de instalar el programa o no, según su confianza en el programador.
Esto es crucial. Es imprescindible la libertad de autorización del operador. Esta libertad se puede ver afectada por engaños, por ejemplo, si el programador le muestra un código diferente al operador del que se va a ejecutar, o si el operador no es hecho consciente de lo que está instalando.
Sin embargo, se presenta una excepción cuando el programador y el operador son la misma persona. Siempre es más satisfactorio lograr que el programa haga algo que ir a instalar ese programa en la PC, ¡y desmiéntanme! Es sumamente aburrido instalar un programa. Es más aburrido cuando el programador cometió un error y hay que ir a reinstalar una versión nueva, y aún más aburrido cuando esto ocurre una y otra vez.
Peldaño 2. Hay que instalar los programas en PCs que están aquí cerca o allá en el cerro. Su instalación puede requerir cambios en el sistema, potencialmente rompiendo la configuración de la máquina misma. Entonces la industria contempla la posibilidad de ejecutar un programa sin necesidad de ser instalado. Esto simplificaría las implementaciones, ahorraría dinero significativamente y se enfocaría el esfuerzo en el desarrollo del programa, y no en su instalación. La necesidad es obvia: un lenguaje que no requiera compilación, cuyo resultado no requiera una instalación, que funcione en todos lados de forma predecible, que no padezca del infierno de las dependencias (librerías), etc. Y luego existen diferentes computadoras con diferentes características, versiones de sistema operativo, etc.
La escalera de la Web
Por otra parte, se genera otra línea de escalafones de tecnología, la de la World Wide Web:
Peldaño A. El formato de la Web es HTML, que es como hablar de un .doc, pero en lugar de binario, en texto plano. El HTML tiene la característica de que el archivo se renderiza por diferentes programas en diferentes plataformas y da resultados suficientemente similares. Se vislumbra a la Web como algo multiplataforma (vamos a dejar la discusión de los múltiples navegadores para otra ocasión).
Peldaño B. La necesidad de la interactividad de las páginas Web. Más que una necesidad por hacer páginas interactivas, es resolver necesidades de interactividad muy específicas. Por ejemplo, el HTML no incluye semántica para mostrar selectores de fecha o validar direcciones de correo electrónico. El manejo masivo de información comienza a pedir menús de árbol expandibles, tan comúnmente usados en el escritorio. Resulta natural querer poner un menú contraíble en una página. Al iniciar como un lenguaje de texto genérico, HTML no podía dar suficiente semántica para que un navegador ofrezca estas capacidades él mismo. Al final todos pecaron: los navegadores populares no ofrecieron una forma de ofrecer tablas de contenido a partir de las etiquetas H1 a H6. HTML no se extendió con nuevos tipos de elementos INPUT, por ejemplo.
Desafortunadamente para HTML, todo mundo le quiso poner diferentes funciones para hacer algo «innovador», como las etiquetas MARQUEE, BLINK y FRAME. Tuvo que llegar el momento de estandarizar, simplificar y reencauzar el lenguaje. Esto también demoró su progreso.
Aquí entra el auge de JavaScript. Por sí misma, la interactividad no debería ser riesgosa para los usuarios. El problema es la aplicación de la misma por medio de un lenguaje de programación (es decir, de propósito general) en lugar de un lenguaje de formato, semántico, específico. Aquí es donde se rompe el peldaño 1: el código escrito en este lenguaje se ejecuta en el navegador sin intervención del usuario.
La unión de ambas escaleras
Al final, se unen las dos escaleras para crear un monstruo. En la combinación navegador-JavaScript, los desarrolladores encuentran una plataforma para resolver el problema de la difícil implementación (descrito en el peldaño 2), mientras que el boom de la interactividad (peldaño B) facilita el auge. Lo anterior, a pesar del romper la libertad de autorización del usuario u operador (descrito en el peldaño 1). JavaScript hace que la implementación de programas sea extremadamente simple, pero a costa del usuario. Ahora el usuario tiene una plataforma de propósito mezclado: por una parte la lectura de la Web y por otra la de aplicaciones Web.
Sobre este ladrillo roto se han estado construyendo más tecnologías. Al principio las aplicaciones web eran páginas cuya interactividad se extendía con JavaScript, pero cada vez se depende más de JavaScript. Ahora hay sitios que generan las páginas mismas desde el script. Esto crea un nivel extra de abstracción que los navegadores no pueden optimizar fácilmente. Al final se traduce en mayor consumo de memoria y CPU, haciendo cada vez más lento al navegador en general. Para mi gusto, es un abuso generalizado de JavaScript, pero que también devela una especie de ruta natural, una ruta de menor resistencia, que es mejor atacar por los cuernos.
¿Qué necesitamos?
Lo que deberíamos tener es:
- Una forma de convertir las aplicaciones Web en aplicaciones de escritorio, sin esfuerzo para el programador.
- Una forma de instalar e integrar fácilmente estas nuevas aplicaciones, sin esfuerzo para el usuario.
- Una forma de distinguir entre la mera interactividad de documento y una aplicación hecha y derecha. La única forma que yo veo, infalible, es desaparecer JavaScript de la lectura de la Web, a favor de mejoras semánticas a HTML y CSS y dejar JavaScript para aplicaciones Web, fuera del navegador.
¿Funcionaría, al menos en concepto?
CSS es un lenguaje sumamente poderoso. Sin necesidad de JavaScript se pueden efectos muy interesantes. Esta página muestra 30 efectos con CSS puro que se pueden hacer hoy por hoy.
¿Podría comprar un boleto de avión sin JavaScript? Poco a poco sería más factible hacer una compra de avión menos frustrante. El wizard estaría programado del lado del servidor y se usaría HTML5 (o tal vez sería necesario inventar algo nuevo) para mejorar la accesibilidad y la interactividad (validaciones, controles de fecha y demás). Imagino que el contenido del wizard se va acumulando en la misma página conforme se llena, alimentado por procesos del lado del servidor. De esta forma podría imprimirse todo el wizard una vez lleno. Habría que estar al pendiente del tiempo restante a la expiración de la sesión. El elemento META podría contener un atributo «session_expiration» y el elemento FORM podría contener un atributo de «partial_action» que permitiría al navegador poner un contador en segundos y un botón de «refresh» para mantener la sesión activa y refrescar el estado del wizard.
¿Podría tener validaciones arbitrarias del lado del cliente? Afortunadamente HTML5 ya permite esto con el atributo pattern, en el que se le puede poner una expresión regular a cumplir como requisito para enviar el formulario. HTML5Pattern muestra algunos ejemplos de validación arbitraria por expresión regular.
¿Funcionarían los SSO como Google Accounts, etc.? Probablemente también se necesiten extensiones semánticas, pero quisiera que que el navegador tuviera un indicador de cuentas en las que tengo sesión en este momento, principalmente basados en OpenID, BrowserID y otros protocolos abiertos. Sería responsabilidad de Facebook desarrollar su propio «provider» para Facebook Connect.
Lo que se lograría
- Yo tendría que aceptar antes de permitir la instalación de la aplicación «Facebook», «Twitter» o «Hangout».
- Al distinguir una aplicación Web de una página Web, las plataformas de aplicación web (que ya no serían los navegadores) permitirían una más fácil integración entre la aplicación y el escritorio (iconos, lectura de documentos, etc).
- Yo podría confiar en mi navegador.
- La sola lectura de páginas web no consumiría tantos recursos como una aplicación Web.
- Poco a poco el HTML iría evolucionando para soportar nativamente lo que hacen las aplicaciones Web. Así ha ocurrido con HTML5, por ejemplo, con los nuevos elementos INPUT.
- Sería más difícil que me rastrearan, como con Google Analytics y demás.
- Funcionaría mejor el botón de Anterior.
- Funcionaría mejor el botón de Imprimir.
- Las páginas serían mucho más «accesibles».
- La barra de estado siempre diría la verdad. Hoy en día hay forma de que uno sombree una liga y lo que se muestra en la barra de estado sea diferente que el sitio al que el navegador mande (*cough* Facebook *cough*).
- Se reduce el footprint de los lectores de páginas Web y se dedica JavaScript (o cualquier otro lenguaje) para aplicaciones especializadas.
Es un tanto paradojico que hables sobre eliminar javascript y simplemente haciendo una busqueda en el html de este documento, pude contar al menos 10 referencias a javascript.
Saludos
Buen post orsonwellesco, Yo agregaría Mario un tanto paranoico, seguramente si no existiera javascript los ataques serían por medio de CSS que por cierto algunos ataques con CSS están bien documentados, como dijera el Dr. Ian Malcolm “life finds a way”.
Saludos.
Mario:
Realmente no hay contradicción. *Parece* una contradicción, pero aunque hoy por hoy tenga elementos SCRIPT, pienso que en un futuro no debería tenerlos. Mi blog no tiene por qué estar yendo a ejecutar código a tu navegador. Por eso la industria iría migrando poco a poco hacia contar con herramientas que faciliten la interactividad sin necesidad de JavaScript.
Afortunadamente tampoco está tan mal la cosa: si visitas mi blog con JavaScript totalmente deshabilitado realmente no notarás diferencia. Cuando mucho, te pedirá que contestes una suma para evitar el spam cuando hagas un comentario. Es decir, el blog no necesita JavaScript para funcionar.
Sí, yo quisiera mantener el Analytics, pero creo que en lugar de usar un SCRIPT para eso, debería haber una etiqueta, tal vez LINK que mostrara al usuario mi intención de pedir su participación en mis estadísticas, y que el usuario accediera o se negara en pleno uso de su libertad. De esta forma, habría una distinción semántica entre el SCRIPT para facilitarle la inserción de un comentario, que el SCRIPT para obligarlo a participar en mis estadísticas de Analytics. Hoy en día no hay tal distinción semántica, y ahí hay un problema.
Mmmmm… coincido con el post anterior, un tanto paranoico, existen muchas capas en programacion con las cuales se podria solventar el tema de seguridad de una aplicacion, sin embargo queda el tema de la malicia, lo mejor es la educacion al usuario, esto es algo asi como la propuesta de la legalizacion de las drogas, por un lado son beneficas para algunos pero destructivas para otros, eliminarian el atractivo monetario para el narco y se volverian corporaciones con reglas, solo cambiaria el ambiente en el cual se desenvolveria el problema, asi veo la propuesta de conversion de aplicaciones web hacia aplicaciones de escritorio. Alvarezp, talvez no sea la comparacion correcta pero es como lo vi asi rapido.
Pingback: JavaScript debe morir | cafeScript
Todo se diseñó mal desde el principio, las redes, el Internet, etc. Pero lo cierto es como dice Jaime A. Arvizu, el problema más fuerte es la malicia de las personas ya que no solo atacan por medios informáticos.