여기에 대부분 의 답변이 전자 앱에 큰 보안 허점을 남기기 때문에이 답변이 주목을 받기 를 바랍니다. 사실 이 답변 은 본질적으로 require()
전자 앱에서 사용하기 위해해야 할 일 입니다. (v7에서 좀 더 깔끔하게 만드는 새로운 전자 API가 있습니다).
나는 당신이 무엇을 할 수 있는지에 대한 최신 전자 API를 사용하여 github에 자세한 설명 / 솔루션 을 require()
썼지 만 여기서는 사전로드 스크립트, contextBridge 및 ipc를 사용하여 접근 방식을 따라야하는 이유를 간단히 설명하겠습니다.
문제
Electron 앱은 노드를 사용하기 때문에 훌륭하지만이 힘은 양날의 검입니다. 주의하지 않으면 앱을 통해 누군가에게 노드에 대한 액세스 권한을 부여하고 노드를 사용하면 악의적 인 행위자가 컴퓨터를 손상 시키거나 운영 체제 파일을 삭제할 수 있습니다 (다른 것들 중에서 생각합니다).
@raddevus가 주석에서 언급했듯이 원격 콘텐츠를 로드 할 때 필요 합니다. 전자 앱이 완전히 오프라인 인 경우 / 로컬 인 경우 단순히 켜는 것이 좋습니다 nodeIntegration:true
. 하지만 여전히 nodeIntegration:false
앱을 사용하는 우발적 / 악의적 인 사용자를 보호하는 역할 을 계속 선택하고 , 컴퓨터에 설치 될 수있는 모든 가능한 맬웨어가 전자 앱과 상호 작용하고 nodeIntegration:true
공격 벡터를 사용하지 못하도록 방지합니다 (매우 드물게 ,하지만 일어날 수 있습니다)!
문제는 어떻게 생겼습니까?
이 문제는 다음과 같은 경우에 나타납니다 (아래 중 하나).
- 적이
nodeIntegration:true
활성화
- 사용
remote
모듈
이 모든 문제는 렌더러 프로세스에서 노드에 대한 중단없는 액세스를 합니다. 렌더러 프로세스가 하이재킹되면 모든 것이 손실 된 것으로 간주 할 수 있습니다.
우리의 솔루션은 무엇입니까
해결책은 렌더러 에게 노드 (예 :)에 대한 직접 액세스 권한을 부여하지 않고 require()
전자 주 프로세스에에 대한 액세스 권한을 부여 require
하고 렌더러 프로세스가 사용해야 할 때마다require
할 때마다 주 프로세스에 대한 요청을 마샬링하는 것입니다.
이것이 Electron의 최신 버전 (7+)에서 작동하는 방식은 렌더러 측에서 ipcRenderer 바인딩을 설정하고 메인 측에서 ipcMain 바인딩을 설정하는 것 입니다. ipcMain 바인딩에서 모듈을 사용하는 리스너 메소드를 설정합니다 require()
. 이것은 우리의 주요 프로세스가require
원하는 모든 것을 습니다.
contextBridge 를 사용하여 ipcRenderer 바인딩을 앱 코드 (사용)에 전달하므로 앱이 require
main 에서 d 모듈 을 사용해야 할 때 IPC (inter-process-communication)를 통해 메시지를 보내고 기본 프로세스가 실행됩니다. 일부 코드를 입력 한 다음 결과와 함께 메시지를 다시 보냅니다.
대충 , 여기에 원하는 작업이 있습니다.
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>
부인 성명
저는 secure-electron-template
전자 앱을 구축하기위한 보안 템플릿 인의 작성자입니다 . 나는이 주제에 관심이 있고 몇 주 동안 (현재 시점에서)이 작업을 해왔습니다.