Electron require () no está definido

107

Estoy creando una aplicación de Electron para mi propio propósito. Mi problema es que cuando estoy usando funciones de nodo dentro de mi página HTML arroja un error de:

'require ()' no está definido.

¿Hay alguna forma de utilizar las funcionalidades de Node en todas mis páginas HTML? Si es posible, déme un ejemplo de cómo hacerlo o proporcione un enlace. Estas son las variables que intento usar en mi página HTML:

  var app = require('electron').remote; 
  var dialog = app.dialog;
  var fs = require('fs');

y estos son los valores que estoy usando en todas mis ventanas HTML dentro de Electron.

Mari Selvan
fuente

Respuestas:

287

A partir de la versión 5, el valor predeterminado de nodeIntegrationcambió de verdadero a falso. Puede habilitarlo al crear la ventana del navegador:

app.on('ready', () => {
    mainWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true
        }
    });
});
Sathiraumesh
fuente
Dado que la versión reciente de Electron tiene nodeIntegration predeterminado en false debido a razones de seguridad, ¿cuál es la forma recomendada de acceder a los módulos de nodo? ¿Hay alguna forma de comunicarse con el proceso principal sin nodeIntegration?
Paulo Henrique
28
@PauloHenrique nodeIntegration: truees un riesgo de seguridad solo cuando está ejecutando un código remoto que no es de confianza en su aplicación. Por ejemplo, suponga que su aplicación abre una página web de terceros. Eso sería un riesgo de seguridad porque la página web de terceros tendrá acceso al tiempo de ejecución del nodo y puede ejecutar algún código malicioso en el sistema de archivos de su usuario. En ese caso, tiene sentido establecer nodeIntegration: false. Si su aplicación no muestra ningún contenido remoto o solo muestra contenido confiable, la configuración nodeIntegration: trueestá bien.
xyres
1
Esto me estaba volviendo loco. Mi aplicación simplemente no mostraría ningún error y no ejecutó mi código. Fue cuando usé un bloque try catch para interceptar el error que finalmente me trajo aquí.
Heriberto Juarez
2
@PauloHenrique: si desea seguir y crear una aplicación segura (que se adhiera a las mejores prácticas de seguridad), siga mi configuración como describo en este comentario: github.com/electron/electron/issues/9920#issuecomment-575839738
Zac
33

Por razones de seguridad, debe mantener nodeIntegration: falsey usar un script de precarga para exponer justo lo que necesita de la API de Node / Electron al proceso de renderizado (vista) a través de la variable de ventana. De los documentos de Electron :

Los scripts de precarga continúan teniendo acceso a requireotras funciones de Node.js


Ejemplo

main.js

const mainWindow = new BrowserWindow({
  webPreferences: {
    preload: path.join(app.getAppPath(), 'preload.js')
  }
})

preload.js

const { remote } = require('electron');

let currWindow = remote.BrowserWindow.getFocusedWindow();

window.closeCurrentWindow = function(){
  currWindow.close();
}

renderer.js

let closebtn = document.getElementById('closebtn');

closebtn.addEventListener('click', (e) => {
  e.preventDefault();
  window.closeCurrentWindow();
});
Rafael Croffi
fuente
1
Si eres un novato en electron como yo: el archivo de renderizado generalmente se incluye en el html de la manera clásica:<script src="./renderer.js"></script>
MrAn3
22

Espero que esta respuesta reciba algo de atención, porque la gran mayoría de las respuestas aquí dejan grandes agujeros de seguridad en su aplicación electrónica. De hecho, esta respuesta es esencialmente lo que debería estar haciendo para usar require()en sus aplicaciones electrónicas. (Solo hay una nueva API de electrones que lo hace un poco más limpio en la v7).

Escribí una explicación / solución detallada en github usando las apis electrónicas más actuales de cómo puedes hacer require()algo, pero explicaré brevemente aquí por qué deberías seguir un enfoque utilizando un script de precarga, contextBridge e ipc.

El problema

Las aplicaciones electrónicas son geniales porque podemos usar el nodo, pero este poder es un arma de doble filo. Si no tenemos cuidado, le damos acceso a alguien al nodo a través de nuestra aplicación, y con el nodo un mal actor puede dañar su máquina o borrar los archivos de su sistema operativo (entre otras cosas, me imagino).

Como lo mencionó @raddevus en un comentario, esto es necesario al cargar contenido remoto . Si su aplicación electrónica está completamente fuera de línea / local , entonces probablemente esté bien simplemente encendiéndola nodeIntegration:true. Sin embargo, todavía optaría por seguir nodeIntegration:falseactuando como una protección para los usuarios accidentales / maliciosos que usan su aplicación, y evitar que cualquier posible malware que pueda instalarse en su máquina interactúe con su aplicación electron y use el nodeIntegration:truevector de ataque (increíblemente raro , pero podría suceder)!

Como se ve el problema

Este problema se manifiesta cuando usted (cualquiera de los siguientes):

  1. Tener nodeIntegration:truehabilitado
  2. Usa el remotemódulo

Todos estos problemas dan acceso ininterrumpido al nodo desde su proceso de renderizado. Si su proceso de renderizado alguna vez es secuestrado, puede considerar que todo está perdido.

Cual es nuestra solucion

La solución es no darle al renderizador acceso directo al nodo (es decir require()), sino darle a nuestro proceso principal de electrones acceso a require, y cada vez que nuestro proceso renderizador necesite usar require, dirigir una solicitud al proceso principal.

La forma en que esto funciona en las últimas versiones (7+) de Electron es en el lado del renderizador, configuramos los enlaces ipcRenderer , y en el lado principal configuramos los enlaces ipcMain . En los enlaces ipcMain configuramos métodos de escucha que usan módulos we require(). Esto está muy bien porque nuestro proceso principal puede hacer requiretodo lo que quiera.

Usamos el contextBridge para pasar los enlaces de ipcRenderer al código de nuestra aplicación (para usar), por lo que cuando nuestra aplicación necesita usar los requiremódulos d en main, envía un mensaje a través de IPC (comunicación entre procesos) y el proceso principal se ejecuta algún código, y luego enviamos un mensaje con nuestro resultado.

A grandes rasgos , esto es lo que quiere hacer.

main.js

const {
  app,
  BrowserWindow,
  ipcMain
} = require("electron");
const path = require("path");
const fs = require("fs");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;

async function createWindow() {

  // Create the browser window.
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false, // is default value after Electron v5
      contextIsolation: true, // protect against prototype pollution
      enableRemoteModule: false, // turn off remote
      preload: path.join(__dirname, "preload.js") // use a preload script
    }
  });

  // Load app
  win.loadFile(path.join(__dirname, "dist/index.html"));

  // rest of code..
}

app.on("ready", createWindow);

ipcMain.on("toMain", (event, args) => {
  fs.readFile("path/to/file", (error, data) => {
    // Do something with file contents

    // Send result back to renderer process
    win.webContents.send("fromMain", responseObj);
  });
});

preload.js

const {
    contextBridge,
    ipcRenderer
} = require("electron");

// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld(
    "api", {
        send: (channel, data) => {
            // whitelist channels
            let validChannels = ["toMain"];
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, data);
            }
        },
        receive: (channel, func) => {
            let validChannels = ["fromMain"];
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender` 
                ipcRenderer.on(channel, (event, ...args) => func(...args));
            }
        }
    }
);

index.html

<!doctype html>
<html lang="en-US">
<head>
    <meta charset="utf-8"/>
    <title>Title</title>
</head>
<body>
    <script>
        window.api.receive("fromMain", (data) => {
            console.log(`Received ${data} from main process`);
        });
        window.api.send("toMain", "some data");
    </script>
</body>
</html>

Descargo de responsabilidad

Soy el autor de secure-electron-templateuna plantilla segura para crear aplicaciones electrónicas. Me importa este tema y he estado trabajando en esto durante algunas semanas (en este momento).

Zac
fuente
Soy nuevo desarrollador de ElectronJS y estaba trabajando a través de un tutorial de PluralSite que ya no se ejecuta debido a que se modificó la configuración de integración de nodos. Su publicación es muy buena y también estoy leyendo su publicación asociada de Github y los documentos de seguridad oficiales de Electron asociados. Configurar la integración de nodos exactamente correctamente (para que las aplicaciones funcionen correctamente y sean seguras) tiene muchas partes móviles (especialmente para los principiantes). Siguiendo la oración de los documentos de Electron "Es primordial que no habilite la integración de Node.js en ningún renderizador (BrowserWindow, BrowserView o <webview>) que cargue contenido remoto " . (Mi énfasis)
raddevus
Supongo que lo contrario es cierto ... "si BrowserWindow no carga contenido remoto, entonces es seguro incluir la integración de nodo". Si esa oración es cierta, es posible que desee modificar un poco sus publicaciones para enfatizar este punto porque, en mi caso, tengo dos aplicaciones que entran en esa categoría y no es necesario eliminar la integración de nodos. Gracias por tu ayuda.
raddevus
1
@raddevus Gracias, espero que la plantilla te ayude a crear aplicaciones electrónicas seguras (si eliges usarla). Sí, tiene razón en su énfasis. Sin embargo, diré que la desactivación nodeIntegrationevita que el usuario se cause daño accidental o intencionalmente mientras usa la aplicación, y es una protección adicional en caso de que algún malware se adjuntara a su proceso electrónico y pudiera realizar XSS sabiendo que este vector estaba abierto (increíblemente raro, pero ahí es donde se fue mi cerebro)!
Zac
1
@raddevus Gracias, estoy actualizando mis publicaciones para reflejar su comentario.
Zac
9

¿Está utilizando nodeIntegration: falsedurante la inicialización de BrowserWindow? Si es así, establézcalo en true(el valor predeterminado es true).

E incluya sus scripts externos en el HTML de esta manera (no como <script> src="./index.js" </script>):

<script>
   require('./index.js')
</script>
RoyalBingBong
fuente
Estoy usando pdf js sin conexión con esto.Entonces, cuando estoy usando nodeIntegration: true, entonces PDFJS.getDocument no es un error de función, llegará.Cómo configurar nodeIntegration: true en mi página html cuando pdfjs está completamente cargado.
Mari Selvan
¿Ha mirado este ejemplo ? Es posible que pueda importar el paquete a través de var pdfjsLib = require('pdfjs-dist')y usarlo de esta manera.
RoyalBingBong
¿Por qué recomienda usar en requirelugar de <script src="..."></script>? Esto también tiene una pregunta sin respuesta aquí .
bluenote10
@ bluenote10 Webpack responde a esta pregunta : es difícil saber de qué depende un script, se debe administrar el orden de dependencia y el código innecesario se descargará y ejecutará.
haykam
7

En primer lugar, la solución @Sathiraumesh deja su aplicación electrónica con un gran problema de seguridad. Imagine que su aplicación está agregando algunas funciones adicionales messenger.com, por ejemplo, el ícono de la barra de herramientas cambiará o parpadeará cuando tenga un mensaje no leído. Entonces, en su main.jsarchivo, crea una nueva BrowserWindow así (observe que intencionalmente escribí mal messenger.com):

app.on('ready', () => {
    const mainWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true
        }
    });
    mainWindow.loadURL(`https://messengre.com`);
});

¿Qué pasa si messengre.comes un sitio web malicioso que quiere dañar su computadora? Si configura nodeIntegration: trueeste sitio tiene acceso a su sistema de archivos local y puede ejecutar esto:

require('child_process').exec('rm -r ~/');

Y su directorio de inicio se ha ido.

Solución
Exponga solo lo que necesita, en lugar de todo. Esto se logra cargando previamente el código javascript con requiredeclaraciones.

// main.js
app.on('ready', () => {
    const mainWindow = new BrowserWindow({
        webPreferences: {
            preload: `${__dirname}/preload.js`
        }
    });
    mainWindow.loadURL(`https://messengre.com`);
});
// preload.js
window.ipcRenderer = require('electron').ipcRenderer;
// index.html
<script>
    window.ipcRenderer.send('channel', data);
</script>

Ahora awful messengre.comno puede borrar todo su sistema de archivos.

Legolando
fuente
-1

Finalmente, lo hice funcionar. Agregue este código a su elemento de script de documento HTML.

Perdón por la respuesta tardía. Utilizo el siguiente código para hacer esto.

window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;

Y use en nodeRequirelugar de usar require.

Funciona bien.

Mari Selvan
fuente
Comparta su código de página HTML.
Vijay
-1

Debe habilitar nodeIntegration en webPreferences para usarlo. vea abajo,

const { BrowserWindow } = require('electron')
let win = new BrowserWindow({
  webPreferences: {
    nodeIntegration: true
  }
})
win.show()

Hubo cambios rotundos en la api en electron 5.0 ( anuncio en el repositorio ). En las versiones recientes, nodeIntegration se establece de forma predeterminada en false .

Docs Debido a la integración Node.js de Electron, hay algunos símbolos adicionales insertados en el módulo DOM como, las exportaciones, requieren. Esto causa problemas a algunas bibliotecas ya que quieren insertar los símbolos con los mismos nombres, para solucionar esto, puede desactivar la integración de nodos en Electron:

Pero si desea mantener la capacidad de usar Node.js y las API de Electron, debe cambiar el nombre de los símbolos en la página antes de incluir otras bibliotecas:

<head>
    <script>
        window.nodeRequire = require;
        delete window.require;
        delete window.exports;
        delete window.module;
    </script>
    <script type="text/javascript" src="jquery.js"></script>
</head>
Kiran Maniya
fuente