Seguridad, capacidades nativas y su responsabilidad
Como desarrolladores web, usualmente disfrutamos la seguridad fuerte de la red del buscador - los riesgos asociados con el código que escribimos son relativamente bajos. A nuestras páginas web se les concede poderes limitados en un sandbox, y confiamos en que nuestros usuarios disfrutan un buscador construido por un gran equipo de ingenieros que es capaz de responder rápidamente a recientes amenazas de seguridad descubiertas.
Cuando se trabaje con Electron, es importante entender que Electron no es un navegador web. Te permite construir aplicaciones de escritorio llenas de utilidades con tecnologías web familiares, pero tu código tiene mucho más poder. JavaScript puede acceder a los archivos del sistema, actividades del usuario y más. Esto permite que construyas aplicaciones nativas de alta calidad, pero los riesgos inherentes de seguridad escalan con el poder adicional concedido a tu código.
Con eso en mente, ten en cuenta que mostrar contenido arbitrario proveniente de fuentes poco confiables viene con un riesgo severo que Electron no está diseñado para manejar. De hecho, las aplicaciones de electron más populares (Atom, Slack, Visual Studio Code, etc) muestran contenido local primero (o confiable, asegurado contenido remoto sin integración nodal) - si tu aplicación ejecuta código de una fuente online es tu responsabilidad asegurar que el código no es malicioso.
#
Reportando problemas de seguridadPara información sobre cómo revelar las vulnerabilidad de Electrón dirigirse a SECURITY.md
#
Actualizaciones y problemas de seguridad ChromiumElectron keeps up to date with alternating Chromium releases. For more information, see the Electron Release Cadence blog post.
#
La seguridad es la responsabilidad de TodosEs importante recordar que la seguridad de tu aplicación Electron es el resultado de la seguridad general de la base de framework (Chromium, Node.js), Electron mismo, todas las dependencias NPM y tu código. Por tanto, es tu responsabilidad seguir algunas importantes mejores prácticas:
Mantenga su aplicación actualizada con la última versión liberada de Electron. Cuando libere su producto, también está compartiendo un conjunto compuesto de Electron, librerías compartidas de Chromium y Node.js. Vulnerabilidades afectando a estos componentes pueden impactar en la seguridad de su aplicación. Actualizando Electron a la última versión, asegura que las vulnerabilidades críticas (tales como nodeIntegration bypasses) ya estén reparadas y no puedan ser explotadas en su aplicación. Para más informacón, vea "Use a current version of Electron".
Evalue sus dependencias.Mientras NPM provee más de medio millón de paquetes reusables, es su responsabilidad la elección de librerías confiables de terceros. Si utiliza librerías desactualizadas afectadas por vulnerabilidades conocidas o basado en código escasamente mantenido, la seguridad de su aplicación puede estar en peligro.
Adopte prácticas de programación segura. La primera línea de defensa de su aplicación es su propio código. Vulnerabilidades usuales, tales como Cross-Site Scripting (XSS), tienen un alto impacto en la seguridad en las aplicaciones Electron así es altamente recomendable adoptar políticas confiables de buenas prácticas en el desarrollo de software y realizar pruebas de seguridad.
#
Aislamiento para contenido no confiableUn problema de seguridad existe siempre que recibes código de un lugar no confiable (e.g. un servidor remoto) y lo ejecutas localmente. Como un ejemplo, considera una página web remota siendo mostrada dentro de un default BrowserWindow
. Si un atacante de algún modo se las arregla para cambiar dicho contenido (bien sea atacando la fuente directamente, o interviniendo entre su aplicación y el destino real), será capaz de ejecutar códigos nativos en la máquina del usuario.
⚠️ bajo ninguna circunstancia deberías cargar y ejecutar código remoto con la integración Node.js activada. En vez de eso, usa solo archivos locales (empaquetados juntos con tu aplicación) para ejecutar el código Node.js. Para mostrar contenido remoto, use la etiqueta
<webview>
oBrowserView
, asegúrese de deshabilitar elnodeIntegration
y habilitarcontextIsolation
.
#
Advertencias de seguridad de ElectronDesde Electron 2.0, los desarrolladores verán advertencias y recomendaciones impresas en la consola de desarrolladores. Estos solo se muestran cuando el nombre del binario es Electron, indicando que un desarrollador está mirando la consola.
Usted puede activar o desactivar estas advertencias forzosamente configurando ELECTRON_ENABLE_SECURITY_WARNINGS
o ELECTRON_DISABLE_SECURITY_WARNINGS
ya sea en process.env
o en el objeto window
.
#
Lista: Recomendaciones de SeguridadAl menos debes seguir los siguientes pasos para mejorar la seguridad de su aplicación:
- Solo carga contenido seguro
- Desactiva la integración Node.js en todas las renderizadores que muestran el contenido remoto
- Permite el aislamiento de contexto en todos los renderizadores que muestran el contenido remoto
- Enable sandboxing
- Usar
ses.setPermissionRequestHandler()
en todas las sesiones que cargan contenido remoto - No desactives
webSecurity
- Define un
Content-Security-Policy
y usa reglas estrictas (i.e.script-src 'self'
) - No establezca
allowRunningInsecureContent
atrue
- No active ajustes experimentales
- No use
enableBlinkFeatures
<webview>
: No useallowpopups
<webview>
: Verificar opciones y parámetros- Deshabilitar o limitar navegación
- Deshabilitar o limitar la generación de nuevas ventanas
- No utilice
openExternal
con contenido no confiable - Usar una versión actual de Electron
Para automatizar la detección de configuraciones erróneas y de modelos inseguros, es posible usar electronegativity. Para detalles adicionales sobre potenciales debilidades y errores en la implementación durante el desarrollo de aplicaciones usando Electron, consulte guía para desarrolladores y auditores
#
1) Cargar solo contenido seguroCualquier recurso no incluido con tu aplicación debería ser cargado usando un protocolo de seguridad como HTTPS
. En otras palabras, no uses protocolos inseguros como HTTP
. De manera similar, recomendamos el uso de WSS
antes de WS
, FTPS
antes de FTP
, y así.
#
¿Por què?HTTPS
tiene tres beneficios principales:
1) Autentica el servidor remoto, asegurando que tu aplicación conecte al anfitrión correcto en vez de un falsificador. 2) Asegura integridad de data, afirmando que la data no fue modificada mientras estaba en tránsito entre tu aplicación y el anfitrión. 3) Encripta el tráfico entre tu usuario y el anfitrión destinatario, haciéndolo más difícil escuchar a escondidas las información establecida entre tu aplicación y el anfitrión.
#
¿Còmo?// BadbrowserWindow.loadURL('http://example.com')
// GoodbrowserWindow.loadURL('https://example.com')
<!-- Bad --><script crossorigin src="http://example.com/react.js"></script><link rel="stylesheet" href="http://example.com/style.css">
<!-- Good --><script crossorigin src="https://example.com/react.js"></script><link rel="stylesheet" href="https://example.com/style.css">
#
2) No habilitar la integración Node.js para contenido RemoteEsta recomendación es el comportamiento por defecto desde Electron 5.0.0.
Es primordial que no active la integración Node.js en ningún renderizador (BrowserWindow
, BrowserView
, o <webview>
) que carga contenido remote. La meta es limitar los poderes que concedes al contenido remoto, aunque lo hace dramáticamente más difícil para un atacante lastimar a tus usuarios, ellos deberían ganar la habilidad de ejecutar JavaScript en tu página web.
Luego de esto, puedes conceder permisos adicionales para anfitriones específicos. For example, if you are opening a BrowserWindow pointed at https://example.com/
, you can give that website exactly the abilities it needs, but no more.
#
¿Por què?Un ataque cross-site-scripting (XSS) es más peligroso si un atacante puede altar fuera del proceso de renderizado y ejecutar el código en la computadora del usuario. Ataques cross-site-scripting son muy comunes - y durante un problema, su poder es usualmente limitado a molestar a la página en dónde los están ejecutando. Desactivar la integración Node.js ayuda a prevenir un XSS de ser escalado en un llamado ataque de "Ejecución de Código Remoto".
#
¿Còmo?// Badconst mainWindow = new BrowserWindow({ webPreferences: { nodeIntegration: true, nodeIntegrationInWorker: true }})
mainWindow.loadURL('https://example.com')
// Goodconst mainWindow = new BrowserWindow({ webPreferences: { preload: path.join(app.getAppPath(), 'preload.js') }})
mainWindow.loadURL('https://example.com')
<!-- Incorrecto --><webview nodeIntegration src="page.html"></webview>
<!-- Correcto --><webview src="page.html"></webview>
Cuando desactivas la integración Node.js, todavía puedes exponer APIs a tu página web que consume módulos Node.js o características. Guiones precargados continúan teniendo acceso a require
y otras características de Node.js, permitiendo que los desarrolladores expongan un API personalizado para cargar contenido de manera remota.
En el siguiente ejemplo de un guión pre cargado, la página web cargada más adelante tendrá acceso a el método window.readConfig()
, pero ninguna característica de Node.js.
const { readFileSync } = require('fs')
window.readConfig = function () { const data = readFileSync('./config.json') return data}
#
3) Habilitar el aislamiento del contexto para Contenido RemotoContext isolation es un ajuste de Electron que permite a los desarrolladores ejecutar códigos en guiones de pre carga y en APIs de Electron en un contexto dedicado de JavaScript. En práctica, eso significa que los objetos globales como Array.prototype.push
o JSON.parse
no puede ser modificado por guiones por guiones ejecutándose en el proceso de renderizado.
Electron usa la misma tecnología que los Content Scripts de Chromium para activar este comportamiento.
Even when nodeIntegration: false
is used, to truly enforce strong isolation and prevent the use of Node primitives contextIsolation
must also be used.
#
Why & How?Para más información sobre que es contextIsolation
y como activarlo, por favor vea nuestro documento dedicado Context Isolation.
#
4) Enable SandboxingSandboxing es una característica de Chromium que usa el sistema operativo para limitar significativamente a lo que el proceso renderizador tiene acceso. You should enable the sandbox in all renderers. Loading, reading or processing any untrusted content in an unsandboxed process, including the main process, is not advised.
#
¿Còmo?When creating a window, pass the sandbox: true
option in webPreferences
:
const win = new BrowserWindow({ webPreferences: { sandbox: true }})
#
5) Gestionar las solicitudes de permiso de sesión desde el contenido remotoTu puedes haber visto pedidos de permiso mientras usas Chrome: Ellos avisan lo que sea que la página intente usar como una característica que el usuario tiene que aprobar manualmente (como notificaciones).
La API está basada en los Chromium permissions API e implementa el mismo tipo de permisos.
#
¿Por què?Por defecto, Electron aprobará automáticamente todos los pedidos de permiso a menos que el desarrollador haya configurado manualmente un comerciante personalizado. Aunque es un sólido ajuste automático, los desarrolladores conscientes de la seguridad pueden querer asumir lo contrario.
#
¿Còmo?const { session } = require('electron')
session .fromPartition('some-partition') .setPermissionRequestHandler((webContents, permission, callback) => { const url = webContents.getURL()
if (permission === 'notifications') { // Approves the permissions request callback(true) }
// Verify URL if (!url.startsWith('https://example.com/')) { // Denies the permissions request return callback(false) } })
#
6) No deshabilitar WebSecurityLa recomendación por defecto es Electrón
Puede que haya adivinado que deshabilitando la propiedad webSecurity
en un render process (BrowserWindow
, BrowserView
, or <webview>
) desactiva características de seguridad cruciales.
No deshabilite webSecurity
en aplicaciones de producción.
#
¿Por què?Desactivar webSecurity
deshabilitará la política de mismo-origen y establecer la propiedad allowRunningInsecureContent
a true
. En otras palabras, permite la ejecución de código inseguro desde diferentes dominios.
#
¿Còmo?// Badconst mainWindow = new BrowserWindow({ webPreferences: { webSecurity: false }})
// Goodconst mainWindow = new BrowserWindow()
<!-- Bad --><webview disablewebsecurity src="page.html"></webview>
<!-- Good --><webview src="page.html"></webview>
#
7) Definir una política de seguridad de contenidoUn Contenido de Política de Seguridad (CSP) es una capa adicional de protección contra los ataques cross-site-scripting attacks y ataques de inyecciones de data. Recomendamos que ellos estén activados por cualquier página web cargada dentro de Electron.
#
¿Por què?CSP permite que el servidor dando contenido pueda restringir y controlar los recursos que Electron puede cargar para esa página web dada. https://example.com
debería estar permitido para guiones de pre carga de los orígenes que definiste mientras que los guiones de https://evil.attacker.com
no debería tener permitido ejecutarse. Definir un CSP es una manera fácil de mejorar la seguridad de tus aplicaciones.
El siguiente CSP permitirá que Electron ejecute guiones desde la página web actual y desde apis.example.com
.
// BadContent-Security-Policy: '*'
// GoodContent-Security-Policy: script-src 'self' https://apis.example.com
#
Encabezado CSP HTTPElectron respeta el Content-Security-Policy
HTTP header que puede ser establecido usando el manejador webRequest.onHeadersReceived
de Electron:
const { session } = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => { callback({ responseHeaders: { ...details.responseHeaders, 'Content-Security-Policy': ['default-src \'none\''] } })})
#
CSP Meta EtiquetaEl mecanismo de entrega preferido de CSP es una cabecera HTTP, sin embargo no es posible usar este método al cargar un recurso usando el protocolo file://
. Puede ser útil en algunos casos, como usar el protocolo file://
, para establecer una política en un página directamente en el markup usando un tag <meta>
:
<meta http-equiv="Content-Security-Policy" content="default-src 'none'">
allowRunningInsecureContent
a true
#
8) No establecer La recomendación por defecto es Electrón
Por defecto, Electron no permitirá sitios webs cargados sobre HTTPS
para cargar y ejecutar scripts, CSS, o plugins desde orígenes inseguros (HTTP
). Establecer la propiedad allowRunningInsecureContent
a true
deshabilita esa protección.
Descargar la inicial HTML de un sitio web mediante HTTPS
e intentar descargar recursos subsecuentes mediante HTTP
es también conocido como "contenido mixto".
#
¿Por què?Cargando contenido sobre HTTPS
se asegura la autenticidad y la integridad de los recursos cargados mientras se cifra el trafico en si mismo. Ver la sección en only displaying secure content para más detalles.
#
¿Còmo?// Badconst mainWindow = new BrowserWindow({ webPreferences: { allowRunningInsecureContent: true }})
// Goodconst mainWindow = new BrowserWindow({})
#
9) No Habilitar características experimentalesLa recomendación por defecto es Electrón
Usuarios avanzados de Electron pueden habilitar las características experimentales de Chromium usando la propiedad experimentalFeatures
.
#
¿Por què?Experimental features are, as the name suggests, experimental and have not been enabled for all Chromium users. Furthermore, their impact on Electron as a whole has likely not been tested.
Casos de uso legítimo existen, pero excepto que usted sepa lo que está haciendo, usted no debería habilitar esta propiedad.
#
¿Còmo?// Badconst mainWindow = new BrowserWindow({ webPreferences: { experimentalFeatures: true }})
// Goodconst mainWindow = new BrowserWindow({})
enableBlinkFeatures
#
10) No use La recomendación por defecto es Electrón
Blink es el nombre del motor de renderizado detrás de Chromium. Como con la propiedad experimentalFeatures
, la propiedad enableBlinkFeatures
permite a los desarrolladores habilitar características que han sido deshabilitada por defecto.
#
¿Por què?En general, probablemente hay buenas razones si una función no fue habilitada por defecto. Casos de uso legítimo para habilitar funciones especificas existen. Como un desarrollador, usted debería saber exactamente por qué usted necesita habilitar una función, cuales son las ramificaciones, y como impacta las seguridad de su aplicación. Usted no debería habilitar funciones de forma especulativa bajo ninguna circunstancia.
#
¿Còmo?// Badconst mainWindow = new BrowserWindow({ webPreferences: { enableBlinkFeatures: 'ExecCommandInJavaScript' }})
// Goodconst mainWindow = new BrowserWindow()
allowpopups
#
11) No use La recomendación por defecto es Electrón
Si estas usando <webview>
, puede que necesites las paginas y los scripts cargados en tu tag <webview>
par abrir nuevas ventanas. El atributo allowpopups
les permite crear nuevas BrowserWindows
usando el método window.open()
. tags <webview>
de otra manera no se permite crear nuevas ventanas.
#
¿Por què?Si usted no necesita ventanas emergentes, le conviene no permitir la creación de nuevos BrowserWindows
por defecto. Esto sigue el principio de acceso de mínimamente requerido: No permita que un sitio web cree nuevas ventanas excepto usted sepa que se necesita esa función.
#
¿Còmo?<!-- Bad --><webview allowpopups src="page.html"></webview>
<!-- Good --><webview src="page.html"></webview>
#
12) Verificar las opciones de WebView antes de la creaciónUn WebView creado en un proceso de renderizado que no contenga integración habilitada de Node.js no será capaz de habilitar integración por sí mismo. Sin embargo, a WebView siempre creará un proco de renderizado independiente con su propio webPreferences
.
Es una buena ida controlar la creación de nuevas etiquetas <webview>
desde el proceso principal y verificar que su webPreferences no deshabilitan características de seguridad.
#
¿Por què?Puesto que <webview>
vive en el DOM, ellos pueden ser creados por un script corriendo en tu sitio web incluso si la integración con Node.js está descativada.
Electron habilita a desarrolladores a inhabilitar varias funciones de seguridad que controlan un proceso de renderizado. En la mayoría de los casos, los desarrolladores no necesitan desactivar ninguna de esas características - y por lo tanto no deberías permitir diferentes configuraciones para las etiquetas <webview>
recién creados.
#
¿Còmo?Antes que la etiqueta <webview>
sea adjuntada, Electron va a disparar el evento will-attach-webview
en el webContents
hosting. Use el evento para evitar la creación de webViews
con posibles opciones inseguras.
app.on('web-contents-created', (event, contents) => { contents.on('will-attach-webview', (event, webPreferences, params) => { // Strip away preload scripts if unused or verify their location is legitimate delete webPreferences.preload delete webPreferences.preloadURL
// Disable Node.js integration webPreferences.nodeIntegration = false
// Verify URL being loaded if (!params.src.startsWith('https://example.com/')) { event.preventDefault() } })})
Una vez más, esta lista simplemente minimiza el riesgo, no lo elimina. If your goal is to display a website, a browser will be a more secure option.
#
13) Deshabilitar o limitar la navegaciónSi tu aplicación no tiene la necesidad de navegar o sólo necesita navegar a páginas conocidas, es una buena idea limitar la navegación directamente a ese alcance conocido, inhabilitando cualquier otro tipo de navegación.
#
¿Por què?La navegación es un vector de ataque común. Si un atacante puede convencer a su aplicación para que navegue lejos de su página actual, posiblemente puede forzar a tu aplicación a abrir sitios web en Internet. Incluso si tu webContents
están configurados para ser más seguros (como tener nodeIntegration
deshabilitado o contextIsolation
habilitado), conseguir que tu aplicación abra un sitio web aleatorio hará que el trabajo de explotar tu aplicación sea mucho mas fácil.
Un patrón común de ataque es que el atacante convence a los usuarios de tu aplicación a interactuar con la aplicación de tal manera que navegue a una de las páginas del atacante. Esto usualmente se hace vía links, plugins u otro contenido generado por el usuario.
#
¿Còmo?Si tu aplicación no tiene necesidad de navegación, puedes llamar a event.preventDefault()
en un manejador will-navigate
. Si sabes a que páginas tu aplicación puede navegar, revisa la URL en el manejador de evento y solo deja que ocurra la navegación si coincide con las URL que estás esperando.
Recomendamos que uses el parser para URLs de Node. Comparaciones simples de cadenas puede a veces engañar - una prueba startsWith('https://example.com')
podría dejar pasar https://example.com.attacker.com
.
const URL = require('url').URL
app.on('web-contents-created', (event, contents) => { contents.on('will-navigate', (event, navigationUrl) => { const parsedUrl = new URL(navigationUrl)
if (parsedUrl.origin !== 'https://example.com') { event.preventDefault() } })})
#
14) Deshabilite o limite la creación de nuevas ventasnasSi tienes un conjunto de ventanas conocido, es una buena idea limitar la creación de ventanas adicionales en tu aplicación.
#
¿Por què?Al igual que la navegación, la creación de nuevo webContents
en un vector de ataque común. Los atacantes intentar convencer a tu aplicación a crear nuevas ventanas, frames u otros procesos renderer con más privilegios de lo que antes tenían; o con páginas abiertas que antes no pudieron abrir.
Si no tienes la necesidad de crear ventanas adicionales de la que sabes que tendrás que crear, desactivando la creación te compra un poco de seguridad extra sin costo alguno. Este comúnmente el caso de las aplicaciones que abre un BrowserWindow
y no necesita abrir un número arbitrario de ventanas adicionales en tiempo de ejecución.
#
¿Còmo?webContents
delegará a su controlado de venta abierta antes de crear nuevas ventanas. The handler will receive, amongst other parameters, the url
the window was requested to open and the options used to create it. We recommend that you register a handler to monitor the creation of windows, and deny any unexpected window creation.
const { shell } = require('electron')
app.on('web-contents-created', (event, contents) => { contents.setWindowOpenHandler(({ url }) => { // In this example, we'll ask the operating system // to open this event's url in the default browser. // // See the following item for considerations regarding what // URLs should be allowed through to shell.openExternal. if (isSafeForExternalOpen(url)) { setImmediate(() => { shell.openExternal(url) }) }
return { action: 'deny' } })})
openExternal
con contenido no confiable#
15) No use El openExternal
de Shell permite abrir un protocolo URI dado con las utilidades nativas del escritorio. En macOS, a modo de ejemplo, esta función es similar a la utilidad de comando de terminal open
y abrirá la aplicación especifica basado en la URI y en el tipo de archivo asociado.
#
¿Por què?El uso indebido de openExternal
puede ser apalancado para comprometer el host del usuario. Cuando openExternal se usa con contenido no confiable, puede ser apalancado para ejecutar comandos arbitrarios.
#
¿Còmo?// Badconst { shell } = require('electron')shell.openExternal(USER_CONTROLLED_DATA_HERE)
// Goodconst { shell } = require('electron')shell.openExternal('https://example.com/index.html')
#
16) Utilizar una versión actual de ElectronYou should strive for always using the latest available version of Electron. Whenever a new major version is released, you should attempt to update your app as quickly as possible.
#
¿Por què?Una aplicación construida con una versión anterior de Electron, Chromium y Node.js es un objetivo más fácil que una aplicación que está usando versiones más recientes de esos componentes. Generalmente hablando, los problemas de seguridad y exploits para viejas verciones de Chromium y Node.js están más ampliamente disponibles.
Ambos Chomium y Node.js impresionantes son impresionantes hazañas de ingeniería construidas por miles de talentosos desarrolladores. Dada su popularidad, su seguridad es cuidadosamente probada y analizada por investigadores de seguridad igualmente calificados. Muchos de esos investigadores disclose vulnerabilities responsibly, lo que generalmente quiere decir que los investigadores darán a Chromium y Node.js algo de tiempo para solucionar los problemas antes de publicarlos. Tu aplicación será más segura si está ejecutando una versión reciente de Electron (y por tanto, Chromium y Node.js) para los cuales problemas de seguridad potenciales no son tan conocidos.