Elektron membutuhkan () tidak ditentukan


111

Saya membuat aplikasi Electron untuk tujuan saya sendiri. Masalah saya adalah ketika saya menggunakan fungsi node di dalam halaman HTML saya, kesalahan itu muncul:

'require ()' tidak ditentukan.

Apakah ada cara untuk menggunakan fungsionalitas Node di semua halaman HTML saya? Jika memungkinkan tolong beri saya contoh bagaimana melakukan ini atau berikan tautan. Berikut adalah variabel yang saya coba gunakan di halaman HTML saya:

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

dan ini adalah nilai yang saya gunakan di semua jendela HTML saya di dalam Electron.


Kemungkinan duplikat dari
Requirement

Jawaban:


296

Pada versi 5, default untuk nodeIntegrationdiubah dari benar menjadi salah. Anda dapat mengaktifkannya saat membuat Jendela Browser:

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

Karena versi terbaru Electron memiliki nodeIntegration default ke false karena alasan keamanan, manakah cara yang disarankan untuk mengakses modul node? Apakah ada cara untuk berkomunikasi dengan proses utama tanpa nodeIntegration?
Paulo Henrique

29
@PauloHenrique nodeIntegration: trueadalah risiko keamanan hanya jika Anda menjalankan beberapa kode jarak jauh yang tidak tepercaya pada aplikasi Anda. Misalnya, aplikasi Anda membuka halaman web pihak ketiga. Itu akan menjadi risiko keamanan karena halaman web pihak ketiga akan memiliki akses ke runtime node dan dapat menjalankan beberapa kode berbahaya pada sistem file pengguna Anda. Dalam hal ini masuk akal untuk mengatur nodeIntegration: false. Jika aplikasi Anda tidak menampilkan konten jarak jauh apa pun, atau hanya menampilkan konten tepercaya, setelan nodeIntegration: truetidak apa-apa.
xyres

1
Ini membuatku gila. Aplikasi saya tidak akan menunjukkan kesalahan dan tidak menjalankan kode saya. Itu ketika saya menggunakan blok coba tangkap untuk mencegat kesalahan yang akhirnya membawa saya ke sini.
Heriberto Juarez

2
@PauloHenrique - Jika Anda ingin mengikuti dan membuat aplikasi yang aman (mengikuti praktik terbaik keamanan), ikuti penyiapan saya seperti yang saya jelaskan dalam komentar ini: github.com/electron/electron/issues/9920#issuecomment-575839738
Zac

35

Untuk alasan keamanan, Anda harus menyimpan nodeIntegration: falsedan menggunakan skrip pramuat untuk mengekspos apa yang Anda butuhkan dari Node / Electron API ke proses renderer (tampilan) melalui variabel jendela. Dari dokumen Electron :

Skrip pramuat terus memiliki akses ke requiredan fitur Node.js lainnya


Contoh

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
Jika Anda seorang pemula elektron seperti saya: file penyaji biasanya disertakan dalam html dengan cara klasik:<script src="./renderer.js"></script>
MrAn3

23

Saya harap jawaban ini mendapat perhatian, karena sebagian besar jawaban di sini meninggalkan lubang keamanan besar di aplikasi elektron Anda. Sebenarnya jawaban ini pada dasarnya adalah apa yang harus Anda lakukan untuk digunakan require()dalam aplikasi elektron Anda. (Hanya ada API elektron baru yang membuatnya sedikit lebih bersih di v7).

Saya menulis penjelasan / solusi terperinci di github menggunakan api elektron terbaru tentang bagaimana Anda dapat melakukan require()sesuatu, tetapi saya akan menjelaskan secara singkat di sini mengapa Anda harus mengikuti pendekatan menggunakan skrip preload, contextBridge, dan ipc.

Masalah

Aplikasi elektron sangat bagus karena kita bisa menggunakan node, tetapi kekuatan ini adalah pedang bermata dua. Jika kami tidak berhati-hati, kami memberi seseorang akses ke node melalui aplikasi kami, dan dengan node, aktor jahat dapat merusak mesin Anda atau menghapus file sistem operasi Anda (antara lain, saya bayangkan).

Seperti yang diungkapkan oleh @raddevus dalam sebuah komentar, hal ini diperlukan saat memuat konten jarak jauh . Jika aplikasi elektron Anda sepenuhnya offline / lokal , Anda mungkin baik-baik saja dengan mengaktifkannya nodeIntegration:true. Namun, saya tetap akan memilih untuk tetap nodeIntegration:falsebertindak sebagai perlindungan bagi pengguna yang tidak disengaja / jahat yang menggunakan aplikasi Anda, dan mencegah kemungkinan malware yang mungkin pernah terinstal di mesin Anda untuk berinteraksi dengan aplikasi elektron Anda dan menggunakan nodeIntegration:truevektor serangan (sangat jarang , tapi bisa terjadi)!

Seperti apa masalahnya

Masalah ini terwujud ketika Anda (salah satu dari yang di bawah ini):

  1. Telah nodeIntegration:truediaktifkan
  2. Gunakan remotemodul

Semua masalah ini memberikan akses tanpa gangguan ke node dari proses perender Anda. Jika proses perender Anda pernah dibajak, Anda dapat menganggap semuanya hilang.

Apa solusi kami

Solusinya adalah tidak memberikan perender akses langsung ke node (mis. require()), Tetapi memberikan akses proses utama elektron kami require, dan kapan pun proses penyaji kami perlu menggunakan require, marshal permintaan ke proses utama.

Cara ini bekerja di versi terbaru (7+) dari Electron ada di sisi penyaji kami mengatur ikatan ipcRenderer , dan di sisi utama kami mengatur ikatan ipcMain . Di binding ipcMain kami menyiapkan metode listener yang menggunakan modul we require(). Ini bagus dan bagus karena proses utama kami dapat requiresemua yang diinginkannya.

Kami menggunakan contextBridge untuk meneruskan ikatan ipcRenderer ke kode aplikasi kami (untuk digunakan), dan ketika aplikasi kami perlu menggunakan requiremodul d di main, itu mengirim pesan melalui IPC (antar-proses-komunikasi) dan proses utama berjalan beberapa kode, dan kami kemudian mengirim pesan kembali dengan hasil kami.

Secara kasar , inilah yang ingin Anda lakukan.

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>

Penolakan

Saya penulis secure-electron-template, templat aman untuk membangun aplikasi elektron. Saya peduli dengan topik ini, dan telah mengerjakannya selama beberapa minggu (pada saat ini).


Saya adalah pengembang ElectronJS baru & bekerja melalui tutorial PluralSite yang tidak lagi menjalankan karena pengaturan integrasi node berubah. Kiriman Anda sangat bagus & saya juga membaca kiriman Github Anda yang terkait dan dokumen keamanan Elektron resmi terkait. Menyiapkan integrasi node dengan tepat (sehingga aplikasi akan berfungsi dengan baik dan aman) memiliki banyak bagian yang bergerak (terutama untuk pemula). Mengikuti kalimat dari dokumen Electron "Yang terpenting adalah Anda tidak mengaktifkan integrasi Node.js di perender mana pun (BrowserWindow, BrowserView, atau <webview>) yang memuat konten jarak jauh. " (Penekanan saya)
raddevus

Saya berasumsi bahwa kebalikannya benar ... "jika BrowserWindow tidak memuat konten jarak jauh, maka aman untuk menyertakan integrasi Node". Jika kalimat itu benar, Anda mungkin ingin mengubah posting Anda sedikit untuk menekankan poin ini karena dalam kasus saya, saya memiliki dua aplikasi yang termasuk dalam kategori itu dan tidak perlu menghapus integrasi node. Terima kasih atas bantuan Anda.
raddevus

1
@raddevus Terima kasih, saya harap templatnya membantu Anda membangun aplikasi elektron yang aman (jika Anda memilih untuk menggunakannya)! Ya, Anda benar tentang penekanan Anda. Namun, saya akan mengatakan menonaktifkan nodeIntegrationmencegah pengguna secara tidak sengaja atau sengaja menyebabkan kerusakan pada diri mereka sendiri saat menggunakan aplikasi, dan merupakan perlindungan ekstra jika beberapa malware melekat pada proses elektron Anda dan dapat melakukan XSS dengan mengetahui vektor ini terbuka (luar biasa jarang, tapi di situlah otak saya pergi)!
Zac

1
@raddevus Terima kasih, saya memperbarui posting saya untuk mencerminkan komentar Anda.
Zac

9

Apakah Anda menggunakan nodeIntegration: falsesaat inisialisasi BrowserWindow? Jika demikian, setel ke true(nilai default adalah true).

Dan sertakan skrip eksternal Anda dalam HTML seperti ini (bukan sebagai <script> src="./index.js" </script>):

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

Saya menggunakan pdf js offline dengan ini.Jadi ketika saya menggunakan nodeIntegration: true maka PDFJS.getDocument bukan kesalahan fungsi akan tiba. Cara mengatur nodeIntegration: true di halaman html saya ketika pdfjs sepenuhnya dimuat.
Mari Selvan

Sudahkah Anda melihat contoh ini ? Anda mungkin dapat mengimpor paket melalui var pdfjsLib = require('pdfjs-dist')dan menggunakannya dengan cara ini.
RoyalBingBong

Mengapa Anda merekomendasikan untuk menggunakan requiredaripada <script src="..."></script>? Ini juga memiliki pertanyaan yang belum terjawab di sini .
bluenote10

@ bluenote10 Webpack menjawab pertanyaan ini : sulit untuk mengatakan skrip bergantung pada apa, urutan ketergantungan harus dikelola, dan kode yang tidak perlu akan tetap diunduh dan dijalankan.
haykam

7

Pertama, solusi @Sathiraumesh meninggalkan aplikasi elektron Anda dengan masalah keamanan yang sangat besar. Bayangkan aplikasi Anda menambahkan beberapa fitur tambahan messenger.com, misalnya ikon bilah alat akan berubah atau berkedip ketika Anda memiliki pesan yang belum dibaca. Jadi di main.jsfile Anda, Anda membuat BrowserWindow baru seperti itu (perhatikan saya sengaja salah mengeja messenger.com):

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

Bagaimana jika messengre.comsitus web berbahaya, yang ingin merusak komputer Anda. Jika Anda menyetel nodeIntegration: truesitus ini memiliki akses ke sistem file lokal Anda dan dapat menjalankan ini:

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

Dan direktori home Anda hilang.

Solusi Tampilkan
hanya apa yang Anda butuhkan, alih-alih semuanya. Ini dicapai dengan memuat kode javascript dengan requirepernyataan.

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

Sekarang mengerikan messengre.comtidak dapat menghapus seluruh sistem file Anda.


-1

Akhirnya, saya berhasil. Tambahkan kode ini ke Elemen Skrip dokumen HTML Anda.

Maaf atas balasan terlambat. Saya menggunakan kode di bawah ini untuk melakukan hal ini.

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

Dan gunakan nodeRequirealih-alih menggunakan require.

Ini bekerja dengan baik.


Silakan bagikan Kode Halaman HTML Anda.
Vijay

-1

Anda harus mengaktifkan nodeIntegration di webPreferences untuk menggunakannya. Lihat di bawah,

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

Ada perubahan api yang merusak elektron 5.0 ( Pengumuman tentang Repositori ). Dalam versi terbaru nodeIntegration secara default disetel ke false .

Docs Karena integrasi Node.js dari Electron, ada beberapa simbol tambahan yang dimasukkan ke dalam DOM seperti modul, ekspor, memerlukan. Ini menyebabkan masalah untuk beberapa perpustakaan karena mereka ingin memasukkan simbol dengan nama yang sama.Untuk mengatasi ini, Anda dapat mematikan integrasi node di Electron:

Tetapi jika Anda ingin mempertahankan kemampuan untuk menggunakan Node.js dan Electron API, Anda harus mengganti nama simbol di halaman sebelum memasukkan pustaka lain:

<head>
    <script>
        window.nodeRequire = require;
        delete window.require;
        delete window.exports;
        delete window.module;
    </script>
    <script type="text/javascript" src="jquery.js"></script>
</head>
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.