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:false
bertindak 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:true
vektor serangan (sangat jarang , tapi bisa terjadi)!
Seperti apa masalahnya
Masalah ini terwujud ketika Anda (salah satu dari yang di bawah ini):
- Telah
nodeIntegration:true
diaktifkan
- Gunakan
remote
modul
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 require
semua yang diinginkannya.
Kami menggunakan contextBridge untuk meneruskan ikatan ipcRenderer ke kode aplikasi kami (untuk digunakan), dan ketika aplikasi kami perlu menggunakan require
modul 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).