Electron require () n'est pas défini


108

Je crée une application Electron dans mon propre but. Mon problème est que lorsque j'utilise des fonctions de nœud dans ma page HTML, une erreur de:

'require ()' n'est pas défini.

Existe-t-il un moyen d'utiliser les fonctionnalités de Node dans toutes mes pages HTML? Si cela est possible, veuillez me donner un exemple de la façon de procéder ou fournir un lien. Voici les variables que j'essaie d'utiliser dans ma page HTML:

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

et ce sont les valeurs que j'utilise dans toutes mes fenêtres HTML dans Electron.


Réponses:


287

À partir de la version 5, la valeur par défaut de est nodeIntegrationpassée de true à false. Vous pouvez l'activer lors de la création de la fenêtre du navigateur:

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

Étant donné que la version récente d'Electron a la valeur par défaut de nodeIntegration sur false pour des raisons de sécurité, quelle est la méthode recommandée pour accéder aux modules de nœuds? Existe-t-il un moyen de communiquer avec le processus principal sans nodeIntegration?
Paulo Henrique

28
@PauloHenrique nodeIntegration: trueest un risque de sécurité uniquement lorsque vous exécutez un code distant non approuvé sur votre application. Par exemple, supposons que votre application ouvre une page Web tierce. Ce serait un risque pour la sécurité car la page Web tierce aura accès au runtime du nœud et peut exécuter du code malveillant sur le système de fichiers de votre utilisateur. Dans ce cas, il est logique de définir nodeIntegration: false. Si votre application n'affiche aucun contenu distant ou n'affiche que du contenu de confiance, le réglage nodeIntegration: trueest correct.
xyres

1
Cela me rendait fou. Mon application n'afficherait simplement aucune erreur et n'exécutait pas mon code. C'est quand j'ai utilisé un bloc try catch pour intercepter l'erreur qui m'a finalement amené ici.
Heriberto Juarez

2
@PauloHenrique - Si vous souhaitez suivre et créer une application sécurisée (respectant les bonnes pratiques de sécurité), veuillez suivre ma configuration telle que je la décris dans ce commentaire: github.com/electron/electron/issues/9920#issuecomment-575839738
Zac

33

Pour des raisons de sécurité, vous devez conserver nodeIntegration: falseet utiliser un script de préchargement pour exposer exactement ce dont vous avez besoin de l'API Node / Electron au processus de rendu (vue) via la variable window. Extrait de la documentation Electron :

Les scripts de préchargement continuent d'avoir accès à requireet à d'autres fonctionnalités de Node.js


Exemple

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

1
Si vous êtes un débutant électron comme moi: le fichier de rendu est généralement inclus dans le html de manière classique:<script src="./renderer.js"></script>
MrAn3

22

J'espère que cette réponse retiendra l'attention, car une grande majorité des réponses ici laissent de grandes failles de sécurité dans votre application électronique. En fait, cette réponse est essentiellement ce que vous devriez faire pour l'utiliser require()dans vos applications électroniques. (Il y a juste une nouvelle API électronique qui le rend un peu plus propre dans la v7).

J'ai écrit une explication / solution détaillée dans github en utilisant les API électroniques les plus récentes sur la façon dont vous pouvez require()quelque chose, mais je vais expliquer brièvement ici pourquoi vous devriez suivre une approche utilisant un script de préchargement, contextBridge et ipc.

Le problème

Les applications Electron sont excellentes parce que nous utilisons le nœud, mais ce pouvoir est une épée à double tranchant. Si nous ne faisons pas attention, nous donnons à quelqu'un l'accès au node via notre application, et avec node un mauvais acteur peut corrompre votre machine ou supprimer les fichiers de votre système d'exploitation (entre autres, j'imagine).

Comme indiqué par @raddevus dans un commentaire, cela est nécessaire lors du chargement de contenu distant . Si votre application électronique est entièrement hors ligne / locale , vous pouvez probablement simplement l'allumer nodeIntegration:true. Cependant, je choisirais toujours de continuer nodeIntegration:falseà agir comme une protection pour les utilisateurs accidentels / malveillants utilisant votre application, et d'empêcher tout logiciel malveillant qui pourrait être installé sur votre machine d'interagir avec votre application électronique et d'utiliser le nodeIntegration:truevecteur d'attaque (incroyablement rare , mais cela pourrait arriver)!

À quoi ressemble le problème

Ce problème se manifeste lorsque vous (l'un des éléments ci-dessous):

  1. Ont nodeIntegration:trueactivé
  2. Utilisez le remotemodule

Tous ces problèmes donnent un accès ininterrompu au nœud à partir de votre processus de rendu. Si votre processus de rendu est piraté, vous pouvez considérer que tout est perdu.

Quelle est notre solution

La solution est de ne pas donner au moteur de rendu un accès direct au nœud (c'est-à-dire require()), mais de donner à notre processus électronique l'accès au processus principal require, et à chaque fois que notre processus de rendu doit utiliser require, rassembler une requête vers le processus principal.

La façon dont cela fonctionne dans les dernières versions (7+) d'Electron est du côté du rendu, nous configurons les liaisons ipcRenderer , et du côté principal, nous configurons les liaisons ipcMain . Dans les liaisons ipcMain, nous mettons en place des méthodes d'écoute qui utilisent les modules we require(). C'est très bien parce que notre processus principal peut requiretout ce qu'il veut.

Nous utilisons le contextBridge pour transmettre les liaisons ipcRenderer au code de notre application (à utiliser), et donc, lorsque notre application doit utiliser les requiremodules d dans main, elle envoie un message via IPC (communication inter-processus) et le processus principal s'exécute un peu de code, puis nous renvoyons un message avec notre résultat.

En gros , voici ce que vous voulez faire.

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>

Avertissement

Je suis l'auteur d' secure-electron-templateun modèle sécurisé pour créer des applications électroniques. Je me soucie de ce sujet et j'y travaille depuis quelques semaines (en ce moment).


Je suis un nouveau développeur ElectronJS et je travaillais sur un didacticiel PluralSite qui ne fonctionne plus car les paramètres d'intégration des nœuds ont été modifiés. Votre message est très bon et je lis également votre message Github associé et la documentation officielle sur la sécurité Electron associée. La configuration exacte de l'intégration des nœuds (pour que les applications fonctionnent correctement et soient sécurisées) comporte de nombreuses pièces mobiles (en particulier pour les débutants). Phrase suivante de la documentation Electron "Il est primordial de ne pas activer l'intégration de Node.js dans un moteur de rendu (BrowserWindow, BrowserView ou <webview>) qui charge du contenu distant. " (Je souligne)
raddevus

Je suppose que l'inverse est vrai ... "si BrowserWindow ne charge pas de contenu distant, alors il est prudent d'inclure l'intégration Node". Si cette phrase est vraie, vous voudrez peut-être modifier un peu vos messages pour souligner ce point car dans mon cas, j'ai deux applications qui entrent dans cette catégorie et n'ont pas besoin de supprimer l'intégration des nœuds. Merci de votre aide.
raddevus le

1
@raddevus Merci, j'espère que le modèle vous aidera à créer des applications électroniques sécurisées (si vous choisissez de l'utiliser)! Oui, vous avez raison sur votre emphase. Cependant, je dirai que la désactivation nodeIntegrationempêche l'utilisateur de se blesser accidentellement ou intentionnellement lors de l'utilisation de l'application, et constitue une protection supplémentaire au cas où un logiciel malveillant serait attaché à votre processus électronique et serait en mesure d'exécuter XSS en sachant que ce vecteur était ouvert (incroyablement rare, mais c'est là que mon cerveau est allé)!
Zac le

1
@raddevus Merci, je mets à jour mes messages pour refléter votre commentaire.
Zac le

9

Utilisez-vous nodeIntegration: falsependant l'initialisation de BrowserWindow? Si tel est le cas, définissez-le sur true(la valeur par défaut est true).

Et incluez vos scripts externes dans le HTML comme ceci (pas comme <script> src="./index.js" </script>):

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

J'utilise pdf js hors ligne avec this.Donc, lorsque j'utilise nodeIntegration: true, alors PDFJS.getDocument n'est pas une erreur de fonction qui arrivera.Comment définir nodeIntegration: true dans ma page html lorsque pdfjs est complètement chargé.
Mari Selvan

Avez-vous regardé cet exemple ? Vous pourrez peut-être simplement importer le package via var pdfjsLib = require('pdfjs-dist')et l'utiliser de cette façon.
RoyalBingBong

Pourquoi recommandez-vous d'utiliser à la requireplace de <script src="..."></script>? Cela a également une question sans réponse ici .
bluenote10

@ bluenote10 Webpack répond à cette question : il est difficile de dire de quoi dépend un script, l'ordre des dépendances doit être géré et le code inutile sera toujours téléchargé et exécuté.
haykam

7

Tout d'abord, la solution @Sathiraumesh laisse votre application électronique avec un énorme problème de sécurité. Imaginez que votre application ajoute des fonctionnalités supplémentaires messenger.com, par exemple, l'icône de la barre d'outils change ou clignote lorsque vous avez un message non lu. Donc, dans votre main.jsfichier, vous créez un nouveau BrowserWindow comme ceci (notez que j'ai intentionnellement mal orthographié messenger.com):

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

Et si messengre.comc'est un site Web malveillant qui veut nuire à votre ordinateur. Si vous définissez nodeIntegration: truece site a accès à votre système de fichiers local et pouvez l'exécuter:

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

Et votre répertoire personnel a disparu.

Solution
N'exposez que ce dont vous avez besoin, au lieu de tout. Ceci est réalisé en préchargeant le code javascript avec des requireinstructions.

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

Maintenant terrible messengre.comne peut pas supprimer tout votre système de fichiers.


-1

Enfin, je l'ai fait fonctionner.Ajoutez ce code à votre document HTML Script Element.

Désolé pour la réponse tardive, j'utilise le code ci-dessous pour faire cette chose.

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

Et utilisez nodeRequireau lieu d'utiliser require.

Ça fonctionne bien.


Veuillez partager votre code de page HTML.
Vijay

-1

Vous devez activer le nodeIntegration dans webPreferences pour l'utiliser. voir ci-dessous,

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

Il y a eu un changement de rupture de l'API dans electron 5.0 ( Annonce sur le référentiel ). Dans les versions récentes, nodeIntegration est défini par défaut sur false .

Docs En raison de l'intégration Node.js d'Electron, il y a quelques symboles supplémentaires insérés dans le DOM comme module, exports, require. Cela pose des problèmes pour certaines bibliothèques car elles veulent insérer les symboles avec les mêmes noms.Pour résoudre cela, vous pouvez désactiver l'intégration des nœuds dans Electron:

Mais si vous souhaitez conserver la possibilité d'utiliser les API Node.js et Electron, vous devez renommer les symboles dans la page avant d'inclure d'autres bibliothèques:

<head>
    <script>
        window.nodeRequire = require;
        delete window.require;
        delete window.exports;
        delete window.module;
    </script>
    <script type="text/javascript" src="jquery.js"></script>
</head>
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.