Electron require () ist nicht definiert


107

Ich erstelle eine Electron-App für meinen eigenen Zweck. Mein Problem ist, wenn ich Knotenfunktionen in meiner HTML-Seite verwende, wird der folgende Fehler ausgegeben:

'require ()' ist nicht definiert.

Gibt es eine Möglichkeit, Knotenfunktionen in allen meinen HTML-Seiten zu verwenden? Wenn es möglich ist, geben Sie mir bitte ein Beispiel dafür oder geben Sie einen Link an. Hier sind die Variablen, die ich in meiner HTML-Seite verwenden möchte:

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

und dies sind die Werte, die ich in allen meinen HTML-Fenstern in Electron verwende.


Antworten:


283

Ab Version 5 wurde die Standardeinstellung für nodeIntegrationvon true in false geändert. Sie können es beim Erstellen des Browserfensters aktivieren:

app.on('ready', () => {
    mainWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true
        }
    });
});

Da in der jüngsten Version von Electron aus Sicherheitsgründen nodeIntegration standardmäßig false ist, welche Methode wird empfohlen, um auf Knotenmodule zuzugreifen? Gibt es eine Möglichkeit, ohne nodeIntegration mit dem Hauptprozess zu kommunizieren?
Paulo Henrique

28
@PauloHenrique nodeIntegration: truestellt nur dann ein Sicherheitsrisiko dar, wenn Sie nicht vertrauenswürdigen Remotecode in Ihrer Anwendung ausführen. Angenommen, Ihre Anwendung öffnet eine Webseite eines Drittanbieters. Dies wäre ein Sicherheitsrisiko, da die Webseite eines Drittanbieters Zugriff auf die Knotenlaufzeit hat und schädlichen Code auf dem Dateisystem Ihres Benutzers ausführen kann. In diesem Fall ist es sinnvoll einzustellen nodeIntegration: false. Wenn Ihre App keine Remote-Inhalte anzeigt oder nur vertrauenswürdige Inhalte anzeigt, ist die Einstellung nodeIntegration: truein Ordnung.
Xyres

1
Das hat mich verrückt gemacht. Meine App würde einfach keinen Fehler anzeigen und meinen Code nicht ausführen. Es war, als ich einen Try-Catch-Block benutzte, um den Fehler abzufangen, der mich schließlich hierher brachte.
Heriberto Juarez

2
@PauloHenrique - Wenn Sie einer sicheren App folgen und diese erstellen möchten (unter Einhaltung der Best Practices für die Sicherheit), befolgen Sie bitte mein Setup, wie in diesem Kommentar beschrieben: github.com/electron/electron/issues/9920#issuecomment-575839738
Zac

33

Aus Sicherheitsgründen sollten Sie nodeIntegration: falseein Preload-Skript beibehalten und verwenden, um genau das, was Sie von der Node / Electron-API benötigen, über eine Fenstervariable für den Renderer-Prozess (Ansicht) bereitzustellen. Aus den Electron-Dokumenten :

Preload-Skripte haben weiterhin Zugriff auf requireund andere Node.js-Funktionen


Beispiel

main.js

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

pretoad.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();
});

Wenn Sie wie ich ein Elektronenneuling sind: Die Renderer-Datei ist normalerweise auf klassische Weise im HTML- <script src="./renderer.js"></script>
Code enthalten

22

Ich hoffe, dass diese Antwort einige Aufmerksamkeit erhält, da eine große Mehrheit der Antworten hier große Sicherheitslücken in Ihrer Elektronen-App hinterlässt . Tatsächlich ist diese Antwort im Wesentlichen das, was Sie tun sollten, um sie require()in Ihren Elektronen-Apps zu verwenden. (Es gibt nur eine neue Elektronen-API, die es in Version 7 ein bisschen sauberer macht).

Ich habe eine ausführliche Erklärung / Lösung in Github unter Verwendung der aktuellsten Elektronen-Apis geschrieben, wie Sie require()etwas können, aber ich werde hier kurz erklären, warum Sie einen Ansatz mit einem Preload-Skript, contextBridge und ipc verfolgen sollten.

Das Problem

Elektronen-Apps sind großartig, weil wir Node verwenden können, aber diese Kraft ist ein zweischneidiges Schwert. Wenn wir nicht aufpassen, geben wir jemandem über unsere App Zugriff auf den Knoten, und mit dem Knoten kann ein schlechter Akteur Ihren Computer beschädigen oder Ihre Betriebssystemdateien löschen (unter anderem, wie ich mir vorstellen kann).

Wie von @raddevus in einem Kommentar erwähnt, ist dies beim Laden von Remote- Inhalten erforderlich . Wenn Ihre Elektronen-App vollständig offline / lokal ist , können Sie sie wahrscheinlich einfach einschalten nodeIntegration:true. Ich würde mich jedoch weiterhin dafür entscheiden, weiterhin nodeIntegration:falseals Schutz für versehentliche / böswillige Benutzer zu fungieren, die Ihre App verwenden, und zu verhindern, dass mögliche Malware, die jemals auf Ihrem Computer installiert wird, mit Ihrer Elektronen-App interagiert und den nodeIntegration:trueAngriffsvektor verwendet (unglaublich selten) , könnte aber passieren)!

Wie sieht das Problem aus?

Dieses Problem tritt auf, wenn Sie (eines der folgenden):

  1. haben nodeIntegration:trueaktiviert
  2. Verwenden Sie das remoteModul

All diese Probleme ermöglichen einen ununterbrochenen Zugriff auf den Knoten von Ihrem Renderer-Prozess. Wenn Ihr Renderer-Prozess jemals entführt wird, können Sie davon ausgehen, dass alles verloren ist.

Was ist unsere Lösung?

Die Lösung besteht darin, dem Renderer keinen direkten Zugriff auf den Knoten (dh require()) zu gewähren, sondern unserem Elektronenhauptprozess Zugriff auf eine Anforderung an den Hauptprozess zu gewähren require, und wann immer unser Rendererprozess diese verwenden muss require.

Dies funktioniert in den neuesten Versionen (7+) von Electron auf der Rendererseite, auf der wir ipcRenderer- Bindungen einrichten , und auf der Hauptseite richten wir ipcMain- Bindungen ein. In den ipcMain-Bindungen richten wir Listener-Methoden ein, die von uns verwendete Module verwenden require(). Das ist in Ordnung und gut, weil unser Hauptprozess requirealles kann, was er will.

Wir verwenden die contextBridge , um die ipcRenderer-Bindungen an unseren App-Code zu übergeben (zu verwenden). Wenn unsere App also die required-Module in main verwenden muss, sendet sie eine Nachricht über IPC (Inter-Process-Communication) und der Hauptprozess wird ausgeführt etwas Code, und wir senden dann eine Nachricht mit unserem Ergebnis zurück.

Hier ist ungefähr das , was Sie tun möchten.

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);
  });
});

pretoad.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>

Haftungsausschluss

Ich bin der Autor secure-electron-templateeiner sicheren Vorlage zum Erstellen von Elektronen-Apps. Ich interessiere mich für dieses Thema und arbeite seit einigen Wochen (zu diesem Zeitpunkt) daran.


Ich bin ein neuer ElectronJS-Entwickler und habe ein PluralSite-Tutorial durchgearbeitet, das nicht mehr ausgeführt wird, da die Einstellungen für die Knotenintegration geändert wurden. Ihr Beitrag ist sehr gut und ich lese auch Ihren zugehörigen Github-Beitrag und die zugehörigen offiziellen Sicherheitsdokumente von Electron. Wenn die Knotenintegration genau richtig eingerichtet wird (damit Apps ordnungsgemäß funktionieren und sicher sind), gibt es viele bewegliche Teile (insbesondere für Neulinge). Der folgende Satz aus Electron-Dokumenten lautet: "Es ist von größter Bedeutung, dass Sie die Node.js-Integration in keinem Renderer (BrowserWindow, BrowserView oder <webview>) aktivieren , der Remote-Inhalte lädt. " (Meine Betonung)
raddevus

Ich gehe davon aus, dass die Umkehrung wahr ist ... "Wenn BrowserWindow keine Remote-Inhalte lädt, ist es sicher, die Knotenintegration einzuschließen." Wenn dieser Satz wahr ist, möchten Sie möglicherweise Ihre Beiträge ein wenig ändern, um diesen Punkt hervorzuheben, da ich in meinem Fall zwei Apps habe, die in diese Kategorie fallen und die Knotenintegration nicht entfernen müssen. Danke für Ihre Hilfe.
Raddevus

1
@raddevus Vielen Dank, ich hoffe, die Vorlage hilft Ihnen beim Erstellen sicherer Elektronen-Apps (wenn Sie sie verwenden möchten)! Ja, Sie haben Recht mit Ihrer Betonung. Ich werde jedoch sagen, nodeIntegrationdass das Deaktivieren verhindert, dass der Benutzer sich versehentlich oder absichtlich selbst Schaden zufügt, während er die App verwendet. Dies ist ein zusätzlicher Schutz für den Fall, dass Malware an Ihren Elektronenprozess angehängt wurde und XSS ausführen konnte, wenn er wusste, dass dieser Vektor offen war (unglaublich selten, aber dort ist mein Gehirn hingegangen)!
Zac

1
@raddevus Vielen Dank, ich aktualisiere meine Beiträge, um Ihren Kommentar widerzuspiegeln.
Zac

9

Verwenden Sie nodeIntegration: falsewährend der BrowserWindow-Initialisierung? Wenn ja, setzen Sie es auf true(Standardwert ist true).

Und fügen Sie Ihre externen Skripte wie folgt in den HTML-Code ein (nicht als <script> src="./index.js" </script>):

<script>
   require('./index.js')
</script>

Ich verwende pdf js offline damit. Wenn ich nodeIntegration verwende: true, dann ist PDFJS.getDocument kein Funktionsfehler aufgetreten. So setzen Sie nodeIntegration: true auf meiner HTML-Seite, wenn pdfjs vollständig geladen ist.
Mari Selvan

Haben Sie sich dieses Beispiel angesehen ? Möglicherweise können Sie das Paket einfach über importieren var pdfjsLib = require('pdfjs-dist')und auf diese Weise verwenden.
RoyalBingBong

Warum empfehlen Sie die Verwendung requireanstelle von <script src="..."></script>? Dies hat auch eine offene Frage hier .
bluenote10

@ bluenote10 Webpack beantwortet diese Frage : Es ist schwer zu sagen, wovon ein Skript abhängt, die Abhängigkeitsreihenfolge muss verwaltet werden und unnötiger Code wird weiterhin heruntergeladen und ausgeführt.
Haykam

7

Zunächst einmal hinterlässt die @ Sathiraumesh-Lösung ein großes Sicherheitsproblem für Ihre Elektronenanwendung. Stellen Sie sich vor, Ihre App fügt einige zusätzliche Funktionen hinzu messenger.com, z. B. ändert sich das Symbol in der Symbolleiste oder blinkt, wenn Sie eine ungelesene Nachricht haben. In Ihrer main.jsDatei erstellen Sie also wie folgt ein neues BrowserWindow (beachten Sie, dass ich messenger.com absichtlich falsch geschrieben habe):

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

Was ist, wenn messengre.comes sich um eine bösartige Website handelt, die Ihrem Computer Schaden zufügen möchte ? Wenn Sie festlegen, dass nodeIntegration: truediese Site Zugriff auf Ihr lokales Dateisystem hat und Folgendes ausführen kann:

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

Und Ihr Home-Verzeichnis ist weg.

Lösung
Legen Sie statt allem nur das offen, was Sie benötigen. Dies wird erreicht, indem Javascript-Code mit requireAnweisungen vorgeladen wird .

// 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>

Jetzt messengre.comkann schrecklich nicht Ihr gesamtes Dateisystem löschen.


-1

Schließlich habe ich es zum Laufen gebracht. Fügen Sie diesen Code Ihrem HTML-Dokument-Skriptelement hinzu.

Entschuldigung für die späte Antwort. Ich benutze den folgenden Code, um diese Sache zu tun.

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

Und verwenden nodeRequirestatt verwenden require.

Es funktioniert gut.


Bitte teilen Sie Ihren HTML-Seitencode.
Vijay

-1

Sie müssen die nodeIntegration in webPreferences aktivieren , um sie verwenden zu können. siehe unten,

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

Es gab eine brechende API-Änderung in Elektron 5.0 ( Ankündigung im Repository ). In neueren Versionen ist nodeIntegration standardmäßig auf false gesetzt .

Dokumente Aufgrund der Node.js-Integration von Electron sind einige zusätzliche Symbole in das DOM eingefügt, z. B. Modul, Exportieren, Erfordernis. Dies führt bei einigen Bibliotheken zu Problemen, da sie gleichnamige Symbole einfügen möchten. Um dies zu beheben, können Sie die Knotenintegration in Electron deaktivieren:

Wenn Sie jedoch die Fähigkeit zur Verwendung von Node.js- und Electron-APIs beibehalten möchten, müssen Sie die Symbole auf der Seite umbenennen, bevor Sie andere Bibliotheken einschließen:

<head>
    <script>
        window.nodeRequire = require;
        delete window.require;
        delete window.exports;
        delete window.module;
    </script>
    <script type="text/javascript" src="jquery.js"></script>
</head>
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.