Tôi hy vọng câu trả lời này sẽ thu hút được sự chú ý, bởi vì phần lớn câu trả lời ở đây để lại những lỗ hổng bảo mật lớn trong ứng dụng điện tử của bạn. Trên thực tế , câu trả lời này về cơ bản là những gì bạn nên làm để sử dụng require()
trong các ứng dụng điện tử của mình. (Chỉ có một API điện tử mới giúp nó sạch hơn một chút trong phiên bản v7).
Tôi đã viết lời giải thích / giải pháp chi tiết trong github bằng cách sử dụng ứng dụng điện tử mới nhất về cách bạn có thể làm require()
gì đó, nhưng tôi sẽ giải thích ngắn gọn ở đây tại sao bạn nên làm theo cách tiếp cận bằng cách sử dụng tập lệnh tải trước, contextBridge và ipc.
Vấn đề
Các ứng dụng điện tử rất tuyệt vời vì chúng ta có thể sử dụng nút, nhưng sức mạnh này là một con dao hai lưỡi. Nếu chúng tôi không cẩn thận, chúng tôi sẽ cấp cho ai đó quyền truy cập vào nút thông qua ứng dụng của chúng tôi và với nút, một tác nhân xấu có thể làm hỏng máy của bạn hoặc xóa các tệp hệ điều hành của bạn (tôi tưởng tượng trong số những thứ khác).
Như được đưa ra bởi @raddevus trong một nhận xét, điều này là cần thiết khi tải nội dung từ xa . Nếu ứng dụng điện tử của bạn hoàn toàn ngoại tuyến / cục bộ , thì bạn có thể không sao chỉ cần bật nodeIntegration:true
. Tuy nhiên, tôi vẫn sẽ chọn giữ lại nodeIntegration:false
để hoạt động như một biện pháp bảo vệ cho những người dùng vô tình / độc hại sử dụng ứng dụng của bạn và ngăn chặn bất kỳ phần mềm độc hại nào có thể được cài đặt trên máy của bạn tương tác với ứng dụng electron của bạn và sử dụng nodeIntegration:true
vectơ tấn công (cực kỳ hiếm , nhưng có thể xảy ra)!
Vấn đề trông như thế nào
Sự cố này xuất hiện khi bạn (bất kỳ trường hợp nào dưới đây):
- Đã
nodeIntegration:true
kích hoạt
- Sử dụng
remote
mô-đun
Tất cả những vấn đề này cho phép bạn truy cập không bị gián đoạn vào nút từ quá trình kết xuất của bạn. Nếu quá trình kết xuất của bạn từng bị tấn công, bạn có thể coi như tất cả đã bị mất.
Giải pháp của chúng tôi là gì
Giải pháp là không cung cấp cho trình kết xuất quyền truy cập trực tiếp vào nút (tức là. require()
), Mà cung cấp cho quy trình chính electron của chúng tôi quyền truy cập require
và bất kỳ lúc nào quy trình kết xuất của chúng tôi cần sử dụng require
, điều chỉnh một yêu cầu tới quy trình chính.
Cách hoạt động của điều này trong các phiên bản mới nhất (7+) của Electron là ở phía trình kết xuất, chúng tôi thiết lập các liên kết ipcRenderer và ở phía chính chúng tôi thiết lập các liên kết ipcMain . Trong các ràng buộc ipcMain, chúng tôi thiết lập các phương thức lắng nghe sử dụng các mô-đun chúng tôi require()
. Điều này là tốt và tốt vì quy trình chính của chúng tôi có thể làm require
tất cả những gì nó muốn.
Chúng tôi sử dụng contextBridge để chuyển các ràng buộc ipcRenderer vào mã ứng dụng của chúng tôi (để sử dụng) và vì vậy khi ứng dụng của chúng tôi cần sử dụng các require
mô-đun d trong chính, nó sẽ gửi một thông báo qua IPC (giao tiếp giữa các quá trình) và quá trình chính sẽ chạy một số mã, và sau đó chúng tôi gửi lại một tin nhắn với kết quả của chúng tôi.
Xấp xỉ , đây là những gì bạn muốn làm.
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>
Khước từ
Tôi là tác giả của secure-electron-template
, một mẫu an toàn để xây dựng các ứng dụng điện tử. Tôi quan tâm đến chủ đề này và đã làm việc với chủ đề này trong vài tuần (tại thời điểm này).