Incluir a la barrera: Fortalecer las aplicaciones con SandBox
Ha pasado más de una semana desde CVE-2023-4863: Se ha hecho público el desbordamiento del búfer Heap en WebP, lo que ha causado una avalancha de nuevos lanzamientos en programas para el renderizado de imágenes webp
: macOS, iOS, Chrome, Firefox y varias distribuciones Linux han recibido actualizaciones. Tras las investigaciones realizadas por Citizen Lab, se ha descubierto que un iPhone utilizado por una "organización de la sociedad civil unicada en Washington DC" estuvo bajo ataque por medio de una vulnerabilidad de cero-clicks en iMessage.
Electron, también, entró en acción y publicó nuevas versiones el mismo día: Si tu aplicación renderiza cualquier contenido proporcionado por el usuario, debes actualizar tu versión de Electron - v27.0.0-beta.2, v26.2.1, v25.8.1, v24.8.3 y v22.3.23, todas contienen una versión corregida de libwebp
, la librería responsable de renderizar imágenes webp
.
Ahora que estamos enterados de que una interacción tan inocente como "renderizar una imagen" es una actividad potencialmente peligrosa, aprovechamos esta oportunidad para recordar a todos que Electron incluye un sandbox de procesos que limita el radio de explosión del siguiente gran ataque — sin importar lo que sea.
El sandbox ha estado disponible desde Electron v1 y está activado por defecto en v20, pero sabemos que varias aplicaciones (especialmente aquellas que están disponibles desde hace tiempo) podrían tener un sandbox: false
en cualquier parte del código – o un nodeIntegration: true
que, igualmente, desactiva el sandbox cuando no hay un ajuste de sandbox
explícito. Eso es comprensible: si has estado con nosotros durante un largo tiempo, probablemente has disfrutado el poder de lanzar un require("child_process")
o require("fs")
en el mismo código que ejecuta tu HTML/CSS.
Antes de hablar sobre cómo migrar al sandbox, primero discutamos por qué lo quieres.
El sandbox pone una jaula dura alrededor de todos los procesos de renderizado, garantizando que sin importar lo que suceda dentro, el código es ejecutado en un entorno restringido. Como concepto, es mucho más viejo que Chromium y es proporcionado como una característica en todos los sistemas operativos. El sandbox de Electron y Chromium es construido en base a estas características del sistema. Incluso si nunca has mostrado contenido generado por el usuario, deberías considerar la posibilidad de que tu renderizado puede verse comprometido: Escenarios tan complejos como los ataques a la cadena de suministros y tan sencillos como pequeños errores, pueden causar que tu renderizado realice acciones que no planeabas.
El entorno de pruebas hace que ese escenario sea mucho menos aterrador: un proceso dentro consigue usar libremente ciclos de CPU y memoria — eso es todo. Los procesos no pueden escribir en el disco o mostrar sus propias ventanas. En el caso de nuestro libwep
error, el sandbox se asegura de que un atacante no pueda instalar o ejecutar malware. De hecho, en el caso del ataque Pegasus original contra el iPhone, de los empleados. el ataque se dirigió específicamente a un proceso de imagen que no es de arena para obtener acceso al teléfono, rompiendo primero los límites del iMessage normalmente encendido. Cuando un CVE como el de este ejemplo es anunciado, todavía tienes que actualizar tus aplicaciones Electron a una versión segura, pero mientras tanto, la cantidad de daño que un atacante puede causar es muy limitada.
Migrar una aplicación de vainilla Electron de sandbox: false
a sandbox: true
es una empresa. Lo sé, porque a pesar de haber escrito personalmente el primer borrador de las Directrices de Seguridad Electron, No he conseguido migrar una de mis propias aplicaciones para usarla. Esto ha cambiado este fin de semana y les recomiendo que lo cambien también.
Hay dos cosas que tienes que tomar en cuenta:
-
If you’re using Node.js code in either
preload
scripts or the actualWebContents
, you need to move all that Node.js interaction to the main process (or, if you are fancy, a utility process). Given how powerful renderers have become, chances are high that the vast majority of your code doesn’t really need refactoring.Consult our documentation on Inter-Process Communication. In my case, I moved a lot of code and wrapped it in
ipcRenderer.invoke()
andipcMain.handle()
, but the process was straightforward and quickly done. Be a little mindful of your APIs here - if you build an API calledexecuteCodeAsRoot(code)
, the sandbox won't protect your users much. -
Since enabling the sandbox disables Node.js integration in your preload scripts, you can no longer use
require("../my-script")
. In other words, your preload script needs to be a single file.There are multiple ways to do that: Webpack, esbuild, parcel, and rollup will all get the job done. I used Electron Forge’s excellent Webpack plugin, users of the equally popular
electron-builder
can useelectron-webpack
.
All in all, the entire process took me around four days — and that includes a lot of scratching my head at how to wrangle Webpack’s massive power, since I decided to use the opportunity to refactor my code in plenty of other ways, too.