Electron need () tanımlı değil


116

Kendi amacım için bir Electron uygulaması oluşturuyorum. Benim sorunum, HTML sayfamın içindeki düğüm işlevlerini kullandığımda şu hata veriyor:

'require ()' tanımlı değil.

Tüm HTML sayfalarımda Düğüm işlevlerini kullanmanın bir yolu var mı? Mümkünse lütfen bunun nasıl yapılacağına dair bir örnek verin veya bir bağlantı sağlayın. HTML sayfamda kullanmaya çalıştığım değişkenler şunlardır:

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

ve bunlar Electron içindeki tüm HTML pencerelerimde kullandığım değerlerdir.


Yanıtlar:


311

Sürüm 5'ten itibaren, varsayılan değer nodeIntegrationdoğrudan yanlışa değiştirildi. Tarayıcı Penceresini oluştururken etkinleştirebilirsiniz:

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

Electron'un son sürümü, güvenlik nedeniyle nodeIntegration varsayılanına sahip olduğundan, düğüm modüllerine erişmek için önerilen yol hangisidir? NodeIntegration olmadan ana işlemle iletişim kurmanın bir yolu var mı?
Paulo Henrique

32
@PauloHenrique nodeIntegration: true, yalnızca uygulamanızda güvenilmeyen uzaktan kod çalıştırırken bir güvenlik riskidir. Örneğin, uygulamanızın bir üçüncü taraf web sayfasını açtığını varsayalım. Üçüncü taraf web sayfasının düğüm çalışma zamanına erişimi olacağı ve kullanıcınızın dosya sisteminde bazı kötü amaçlı kod çalıştırabileceği için bu bir güvenlik riski oluşturur. Bu durumda ayarlamak mantıklıdır nodeIntegration: false. Uygulamanız herhangi bir uzak içeriği görüntülemiyorsa veya yalnızca güvenilen içeriği görüntülüyorsa, ayar nodeIntegration: truetamamdır.
xyres

1
Bu beni deli ediyordu. Uygulamam hiçbir hata göstermiyor ve kodumu çalıştırmıyordu. Beni nihayet buraya getiren hatayı engellemek için bir deneme yakalama bloğu kullandığım zamandı.
Heriberto Juarez

3
@PauloHenrique - Güvenli bir uygulama takip etmek ve oluşturmak istiyorsanız (en iyi güvenlik uygulamalarına bağlı kalarak), lütfen bu yorumda açıkladığım gibi kurulumumu izleyin: github.com/electron/electron/issues/9920#issuecomment-575839738
Zac

10.1.15'te çalışmıyor, yine de güvenlik uyarısı alıyorsunuz.
Wilson Chen

38

Güvenlik nedenleriyle, nodeIntegration: falseNode / Electron API'den renderer sürecine (view) pencere değişkeni aracılığıyla tam olarak ihtiyacınız olanı göstermek için bir ön yükleme betiği tutmalı ve kullanmalısınız. Gönderen Elektron docs :

Önyükleme komut dosyaları requireve diğer Node.js özelliklerine erişmeye devam eder


Misal

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
Eğer benim gibi bir elektron acemi iseniz: renderer dosyası genellikle klasik şekilde <script src="./renderer.js"></script>
html'ye

26

Umarım bu yanıt biraz dikkat çeker, çünkü buradaki yanıtların büyük çoğunluğu elektron uygulamanızda büyük güvenlik açıkları bırakmaktadır . Aslında bu cevap , require()elektron uygulamalarınızda kullanmak için yapmanız gereken şeydir . (Sadece v7'de onu biraz daha temiz yapan yeni bir elektron API'si var).

Github'da bir şeyi nasıl yapabileceğinize dair en güncel elektron apisini kullanarak ayrıntılı bir açıklama / çözüm yazdım require(), ancak burada neden bir önyükleme betiği, contextBridge ve ipc kullanarak bir yaklaşımı izlemeniz gerektiğini kısaca açıklayacağım.

Sorun

Elektron uygulamaları harika çünkü düğümü kullanıyoruz, ancak bu güç iki ucu keskin bir kılıç. Dikkatli olmazsak, birisine uygulamamız aracılığıyla düğüme erişim izni veririz ve düğüm ile kötü bir aktör, makinenizi bozabilir veya işletim sistemi dosyalarınızı silebilir (diğer şeylerin yanı sıra, sanırım).

@Raddevus tarafından bir yorumda belirtildiği gibi, bu, uzak içerik yüklenirken gereklidir . Elektron uygulamanız tamamen çevrimdışı / yerel ise , o zaman muhtemelen basitçe açabilirsiniz . Yine de, uygulamanızı kullanan yanlışlıkla / kötü niyetli kullanıcılar için bir koruma önlemi olarak hareket etmeyi ve makinenize yüklenebilecek olası kötü amaçlı yazılımların elektron uygulamanızla etkileşime girmesini ve saldırı vektörünü (inanılmaz derecede nadir , ama olabilir)!nodeIntegration:truenodeIntegration:falsenodeIntegration:true

Sorun neye benziyor

Bu sorun, siz (aşağıdakilerden herhangi biri):

  1. nodeIntegration:trueetkin
  2. remoteModülü kullanın

Tüm bu sorunlar , oluşturucu işleminizden düğüme kesintisiz erişim sağlar. Oluşturucu işleminiz ele geçirilirse, her şeyin kaybolduğunu düşünebilirsiniz.

Bizim çözümümüz nedir

Çözüm, oluşturucuya düğüme (ör. ) Doğrudan erişim sağlamak require()değil, elektron ana işlemimize erişim sağlamak requireve oluşturucu işlemimizin kullanması gerektiğinde requireana işlem için bir istek sıralamaktır.

Electron'un en son sürümlerinde (7+) bunun çalışma şekli oluşturucu tarafında ipcRenderer bağlamalarını kuruyoruz ve ana tarafta ipcMain bağlarını kuruyoruz . İpcMain bağlamalarında, modülleri kullandığımız dinleyici yöntemlerini kurarız require(). Bu güzel ve iyi çünkü ana sürecimiz requireistediği her şeyi yapabilir .

Biz kullanmak contextBridge bizim uygulama koduna (kullanım için) ipcRenderer bağlamaları geçmek ve böylece bizim app ihtiyaçları ne zaman kullanılacağını requireana d modülleri, bu IPC (süreçler arası-iletişim) ve ana süreç çalışır aracılığıyla bir mesaj gönderir bazı kodlar ve ardından sonucumuzla birlikte bir mesaj göndeririz.

Kabaca , işte yapmak istediğiniz şey.

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>

Feragatname

Ben yazar değilim secure-electron-template, yapı elektron uygulamalara güvenli bir şablon. Bu konuyu önemsiyorum ve birkaç haftadır bunun üzerinde çalışıyorum (bu noktada).


Ben yeni ElectronJS geliştiricisiyim ve düğüm entegrasyon ayarları değiştiği için artık çalışmayan bir PluralSite öğreticisi aracılığıyla çalışıyordum. Gönderiniz çok iyi ve ayrıca ilgili Github gönderinizi ve ilgili resmi Electron güvenlik belgelerini okuyorum. Düğüm entegrasyonunun tam olarak doğru şekilde kurulmasını sağlamak (böylece uygulamalar düzgün çalışacak ve güvenli olacaktır) çok sayıda hareketli parçaya sahiptir (özellikle yeni başlayanlar için). Electron belgelerinden alınan cümlenin ardından " Uzak içerik yükleyen herhangi bir oluşturucuda (BrowserWindow, BrowserView veya <webview>) Node.js entegrasyonunu etkinleştirmemeniz çok önemlidir . " (
Vurgularım

Tersinin doğru olduğunu varsayıyorum ... "BrowserWindow uzak içeriği yüklemezse, Düğüm entegrasyonunu eklemek güvenlidir". Bu cümle doğruysa, bu noktayı vurgulamak için yayınlarınızı biraz değiştirmek isteyebilirsiniz çünkü benim durumumda bu kategoriye giren ve düğüm entegrasyonunu kaldırmaya gerek olmayan iki uygulamam var. Yardımınız için teşekkürler.
raddevus

1
@raddevus Teşekkürler, umarım şablon güvenli elektron uygulamaları oluşturmanıza yardımcı olur (kullanmayı seçerseniz)! Evet, vurgu konusunda haklısın. Bununla birlikte, devre dışı bırakmanın nodeIntegration, kullanıcının uygulamayı kullanırken yanlışlıkla veya kasıtlı olarak kendisine zarar vermesini önlediğini ve elektron sürecinize bazı kötü amaçlı yazılımların eklenmesi ve bu vektörün açık olduğunu bilerek XSS gerçekleştirebilmesi durumunda ekstra bir koruma olduğunu söyleyeceğim (inanılmaz derecede nadir, ama beynim oraya gitti)!
Zac

1
@raddevus Teşekkürler, gönderilerimi yorumunuzu yansıtacak şekilde güncelliyorum.
Zac

9

nodeIntegration: falseBrowserWindow başlatılırken kullanıyor musunuz ? Öyleyse, olarak ayarlayın true(varsayılan değer true).

Ve harici komut dosyalarınızı HTML'ye şu şekilde dahil edin (olarak değil <script> src="./index.js" </script>):

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

Bununla çevrimdışı pdf js kullanıyorum.Yani nodeIntegration: true kullandığımda PDFJS.getDocument bir işlev hatası değil. NodeIntegration nasıl ayarlanır : pdfjs tamamen yüklendiğinde html sayfamda true .
Mari Selvan

Bu örneğe baktınız mı ? Paketi üzerinden içe aktarabilir var pdfjsLib = require('pdfjs-dist')ve bu şekilde kullanabilirsiniz.
RoyalBingBong

Neden requireyerine kullanmayı öneriyorsunuz <script src="..."></script>? Bunun da burada cevaplanmamış bir sorusu var .
bluenote10

@ bluenote10 Webpack bu soruyu yanıtlıyor : bir betiğin neye bağlı olduğunu söylemek zor, bağımlılık sırası yönetilmeli ve gereksiz kod yine de indirilip çalıştırılacak.
haykam

8

Öncelikle, @Sathiraumesh çözümü elektron uygulamanızı büyük bir güvenlik sorunuyla karşı karşıya bırakır. Uygulamanızın bazı ekstra özellikler eklediğini hayal edin, messenger.comörneğin, okunmamış mesajınız olduğunda araç çubuğunun simgesi değişecek veya yanıp sönecek. Böylece main.jsdosyanızda, böyle yeni BrowserWindow oluşturursunuz (messenger.com'u kasıtlı olarak yanlış yazdığıma dikkat edin):

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

Ya messengre.combilgisayarınıza zarar vermek isteyen kötü niyetli bir web sitesiyse. nodeIntegration: trueBu sitenin yerel dosya sisteminize erişimi olduğunu ayarlarsanız ve bunu çalıştırabilirseniz:

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

Ve ana dizininiz kayboldu.

Çözüm
Her şey yerine yalnızca ihtiyacınız olanı ortaya çıkarın. Bu, javascript kodunu requireifadelerle önceden yükleyerek elde edilir .

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

Şimdi korkunç messengre.com, tüm dosya sisteminizi silemez.


1

Tek yapmak istediğim, takip ettiğim eğitim nedeniyle html sayfamda bir js dosyası gerektirmekti. Ancak, güvenlik çok önemli olduğu için uzak modülleri kullanmayı planlıyorum. Michael'ın cevabını orada değiştirdim, bu yüzden sadece benim gibi 'gerekli' yerine güvenli bir alternatif aramak için saatler harcayanlar için gönderiyorum. Kod yanlışsa, belirtmekten çekinmeyin.

main.js

const electron = require('electron');
const app=electron.app;
const BrowserWindow=electron.BrowserWindow;
const ipcMain=electron.ipcMain;

const path=require('path');
const url=require('url');

let win;

function createWindow(){
    win=new BrowserWindow({
        webPreferences:{
            contextIsolation: true,
            preload: path.join(__dirname, "preload.js")
        }
    });
    win.loadURL(url.format({
        pathname: path.join(__dirname, 'index.html'),
        protocol: 'file',
        slashes: true
    }));

    win.on('close', function(){
        win=null
    });
}

app.on('ready', createWindow);

preload.js

const electron=require('electron');
const contextBridge=electron.contextBridge;

contextBridge.exposeInMainWorld(
    "api", {
        loadscript(filename){
            require(filename);
        }
    }
);

index.html

<!DOCTYPE html>
<html>
    <head>
        <title>Hello World App</title>
    </head>
    <body>
        <h1>Hello World</h1>
        <button id="btn">Click</button>
    </body>
    <script>
        window.api.loadscript('./index.js');
    </script>
</html>

index.js

const btn = document.getElementById('btn');
btn.addEventListener('click', function(){
    console.log('button clicked');
});

Bunun hala bir güvenlik riski oluşturup oluşturmadığını özellikle merak ediyorum. Teşekkürler.


-1

Sonunda çalışmasını sağladım.Bu kodu HTML dokümanınızın Script Elementine ekleyin

Geç cevap verdiğim için özür dilerim. Bunu yapmak için aşağıdaki kodu kullanıyorum.

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

Ve kullanmak nodeRequireyerine kullanın require.

İyi çalışıyor.


Lütfen HTML Sayfa Kodunuzu paylaşın.
Vijay

-1

Kullanmak için webPreferences içindeki nodeIntegration'ı etkinleştirmeniz gerekir. aşağıya bakınız,

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

Elektron 5.0'da kırılma api değişiklikleri oldu ( Depo Üzerine Duyuru ). Son sürümlerde nodeIntegration varsayılan olarak false değerine ayarlanmıştır .

Docs Electron'un Node.js entegrasyonu nedeniyle, DOM'a eklenmiş bazı ekstra semboller vardır, örneğin modül, dışa aktarım, gerektirir. Bu, sembolleri aynı isimle eklemek istedikleri için bazı kütüphaneler için sorunlara neden olur. Bunu çözmek için Electron'da düğüm entegrasyonunu kapatabilirsiniz:

Ancak, Node.js ve Electron API'lerini kullanma yeteneklerini korumak istiyorsanız, diğer kitaplıkları dahil etmeden önce sayfadaki sembolleri yeniden adlandırmanız gerekir:

<head>
    <script>
        window.nodeRequire = require;
        delete window.require;
        delete window.exports;
        delete window.module;
    </script>
    <script type="text/javascript" src="jquery.js"></script>
</head>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.