Electron require ()가 정의되지 않았습니다.


111

저는 제 목적을 위해 Electron 앱을 만들고 있습니다. 내 문제는 HTML 페이지 내에서 노드 함수를 사용할 때 다음과 같은 오류가 발생한다는 것입니다.

'require ()'가 정의되지 않았습니다.

모든 HTML 페이지에서 노드 기능을 사용하는 방법이 있습니까? 가능한 경우이 작업을 수행하는 방법의 예를 제공하거나 링크를 제공하십시오. 내 HTML 페이지에서 사용하려는 변수는 다음과 같습니다.

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

이것이 Electron의 모든 HTML 창에서 사용하는 값입니다.


답변:


296

버전 5부터의 기본값이 nodeIntegrationtrue에서 false로 변경되었습니다. 브라우저 창을 만들 때 활성화 할 수 있습니다.

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

Electron의 최신 버전에는 보안상의 이유로 nodeIntegration이 false로 기본 설정되어 있으므로 노드 모듈에 액세스하는 데 권장되는 방법은 무엇입니까? nodeIntegration없이 메인 프로세스와 통신 할 수있는 방법이 있습니까?
Paulo Henrique

29
@PauloHenrique nodeIntegration: true는 애플리케이션에서 신뢰할 수없는 원격 코드를 실행할 때만 보안 위험이 있습니다. 예를 들어 애플리케이션이 타사 웹 페이지를 여는 경우를 가정 해 보겠습니다. 타사 웹 페이지가 노드 런타임에 액세스 할 수 있고 사용자의 파일 시스템에서 일부 악성 코드를 실행할 수 있기 때문에 보안 위험이 있습니다. 이 경우을 설정하는 것이 좋습니다 nodeIntegration: false. 앱에 원격 콘텐츠가 표시되지 않거나 신뢰할 수있는 콘텐츠 만 표시되는 경우 설정 nodeIntegration: true이 정상입니다.
xyres 19-06-06

1
이것은 나를 미치게 만들었다. 내 앱은 단순히 오류를 표시하지 않고 내 코드를 실행하지 않았습니다. 마침내 나를 여기로 가져온 오류를 가로 채기 위해 try catch 블록을 사용했을 때였습니다.
Heriberto Juarez

2
@PauloHenrique-보안 앱 (보안 모범 사례 준수)을 따르고 만들려면이 댓글에서 설명한대로 내 설정을 따르세요. github.com/electron/electron/issues/9920#issuecomment-575839738
Zac

35

보안상의 이유로 nodeIntegration: false사전로드 스크립트를 유지 하고 사용하여 Node / Electron API에서 필요한 것만 창 변수를 통해 렌더러 프로세스 (보기)에 노출해야합니다. 로부터 전자 문서 :

사전로드 스크립트는 require및 기타 Node.js 기능에 계속 액세스 할 수 있습니다.


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
나처럼 전자 초보자라면 렌더러 파일은 일반적으로 고전적인 방식으로 html에 포함됩니다.<script src="./renderer.js"></script>
MrAn3

23

여기에 대부분 의 답변이 전자 앱에 보안 허점을 남기기 때문에이 답변이 주목을 받기 를 바랍니다. 사실 이 답변 은 본질적으로 require()전자 앱에서 사용하기 위해해야 ​​할 일 입니다. (v7에서 좀 더 깔끔하게 만드는 새로운 전자 API가 있습니다).

나는 당신이 무엇을 할 수 있는지에 대한 최신 전자 API를 사용하여 github에 자세한 설명 / 솔루션require()썼지 만 여기서는 사전로드 스크립트, contextBridge 및 ipc를 사용하여 접근 방식을 따라야하는 이유를 간단히 설명하겠습니다.

문제

Electron 앱은 노드를 사용하기 때문에 훌륭하지만이 힘은 양날의 검입니다. 주의하지 않으면 앱을 통해 누군가에게 노드에 대한 액세스 권한을 부여하고 노드를 사용하면 악의적 인 행위자가 컴퓨터를 손상 시키거나 운영 체제 파일을 삭제할 수 있습니다 (다른 것들 중에서 생각합니다).

@raddevus가 주석에서 언급했듯이 원격 콘텐츠를 로드 필요 합니다. 전자 앱이 완전히 오프라인 인 경우 / 로컬 인 경우 단순히 켜는 것이 좋습니다 nodeIntegration:true. 하지만 여전히 nodeIntegration:false앱을 사용하는 우발적 / 악의적 인 사용자를 보호하는 역할 을 계속 선택하고 , 컴퓨터에 설치 될 수있는 모든 가능한 맬웨어가 전자 앱과 상호 작용하고 nodeIntegration:true공격 벡터를 사용하지 못하도록 방지합니다 (매우 드물게 ,하지만 일어날 수 있습니다)!

문제는 어떻게 생겼습니까?

이 문제는 다음과 같은 경우에 나타납니다 (아래 중 하나).

  1. 적이 nodeIntegration:true활성화
  2. 사용 remote모듈

이 모든 문제는 렌더러 프로세스에서 노드에 대한 중단없는 액세스를 합니다. 렌더러 프로세스가 하이재킹되면 모든 것이 손실 된 것으로 간주 할 수 있습니다.

우리의 솔루션은 무엇입니까

해결책은 렌더러 에게 노드 (예 :)에 대한 직접 액세스 권한을 부여하지 않고 require()전자 주 프로세스에에 대한 액세스 권한을 부여 require하고 렌더러 프로세스가 사용해야 할 때마다require 할 때마다 주 프로세스에 대한 요청을 마샬링하는 것입니다.

이것이 Electron의 최신 버전 (7+)에서 작동하는 방식은 렌더러 측에서 ipcRenderer 바인딩을 설정하고 메인 측에서 ipcMain 바인딩을 설정하는 입니다. ipcMain 바인딩에서 모듈을 사용하는 리스너 메소드를 설정합니다 require(). 이것은 우리의 주요 프로세스가require 원하는 모든 것을 습니다.

contextBridge 를 사용하여 ipcRenderer 바인딩을 앱 코드 (사용)에 전달하므로 앱이 requiremain 에서 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전자 앱을 구축하기위한 보안 템플릿 인의 작성자입니다 . 나는이 주제에 관심이 있고 몇 주 동안 (현재 시점에서)이 작업을 해왔습니다.


저는 새로운 ElectronJS 개발자이며 더 이상 bec 노드 통합 설정을 실행하지 않는 PluralSite 자습서를 통해 작업했습니다. 귀하의 게시물은 매우 훌륭하며 관련 Github 게시물 및 관련 공식 Electron 보안 문서도 읽고 있습니다. 노드 통합을 정확하게 설정하면 (앱이 제대로 작동하고 보안이 유지됨) 움직이는 부분이 많이 있습니다 (특히 초보자를위한 것). Electron 문서에서 " 원격 콘텐츠를로드 하는 렌더러 (BrowserWindow, BrowserView 또는 <webview>)에서 Node.js 통합을 활성화하지 않는 것이 가장 중요합니다 . "(내 강조)
raddevus

나는 그 반대가 사실이라고 가정하고있다. "브라우저 윈도우가 원격 컨텐츠를로드하지 않는다면 노드 통합을 포함하는 것이 안전하다". 이 문장이 사실이라면이 점을 강조하기 위해 게시물을 약간 변경하는 것이 좋습니다. 제 경우에는 해당 범주에 속하는 두 개의 앱이 있고 노드 통합을 제거 할 필요가 없기 때문입니다. 당신의 도움을 주셔서 감사합니다.
raddevus

1
@raddevus 감사합니다. 템플릿이 보안 전자 앱을 구축하는 데 도움이되기를 바랍니다 (사용하기로 선택한 경우)! 네, 당신은 당신의 강조에 맞습니다. 그러나 비활성화 nodeIntegration하면 사용자가 앱을 사용하는 동안 실수로 또는 고의로 자신에게 해를 입히는 것을 방지 할 수 있으며, 일부 맬웨어가 전자 프로세스에 부착되어이 벡터가 열려 있다는 것을 알고 XSS를 수행 할 수있는 경우를 대비하여 추가 보호 수단입니다 (놀랍게도 드물지만 그것이 내 두뇌가 갔던 곳입니다)!
Zac

1
@raddevus 감사합니다. 귀하의 의견을 반영하기 위해 게시물을 업데이트하고 있습니다.
Zac

9

nodeIntegration: falseBrowserWindow 초기화 중에 사용 하고 있습니까? 그렇다면 true(기본값은 true)로 설정하십시오 .

다음과 같이 HTML에 외부 스크립트를 포함합니다 (은 아님 <script> src="./index.js" </script>).

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

I 'm using pdf js offline with this. so I 'm using nodeIntegration : true then PDFJS.getDocument is not a function error will arrive. pdfjs가 완전히로드되면 내 html 페이지에서 nodeIntegration : true 를 설정하는 방법 .
Mari Selvan

예를 보셨습니까 ? var pdfjsLib = require('pdfjs-dist')이 방법으로 패키지를 가져 와서 사용할 수 있습니다.
RoyalBingBong

require대신 사용 하는 것이 좋습니다 <script src="..."></script>. 여기 에는 답이없는 질문도 있습니다 .
bluenote10

@ bluenote10 Webpack은이 질문에 답합니다 . 스크립트가 무엇에 의존하는지 말하기 어렵고 종속성 순서를 관리해야하며 불필요한 코드가 계속 다운로드되고 실행됩니다.
haykam

7

우선 @Sathiraumesh 솔루션은 전자 애플리케이션에 엄청난 보안 문제를 남깁니다. 앱이에 몇 가지 추가 기능을 추가한다고 가정 해보십시오. messenger.com예를 들어 읽지 않은 메시지가 있으면 툴바의 아이콘이 변경되거나 깜박입니다. 따라서 main.js파일에서 다음과 같이 새 BrowserWindow를 만듭니다 (내가 의도적으로 messenger.com을 잘못 입력했음을 알 수 있음).

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

messengre.com컴퓨터에 해를 끼치고 자하는 악성 웹 사이트 라면 어떨까요? nodeIntegration: true이 사이트 를 설정 하면 로컬 파일 시스템에 대한 액세스 권한이 있고 다음을 실행할 수 있습니다.

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

그리고 홈 디렉토리가 사라졌습니다.

솔루션
모든 것이 아니라 필요한 것만 노출하십시오. 이것은 require문과 함께 자바 스크립트 코드를 미리로드함으로써 달성됩니다 .

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

이제 awful messengre.com은 전체 파일 시스템을 삭제할 수 없습니다.


-1

마지막으로 작동하도록 만들었습니다.이 코드를 HTML 문서 스크립트 요소에 추가합니다.

늦은 회신에 대해 죄송합니다.이 작업을 수행하기 위해 아래 코드를 사용합니다.

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

그리고 사용 nodeRequire사용하는 대신 require.

잘 작동합니다.


HTML 페이지 코드를 공유하십시오.
Vijay

-1

이를 사용하려면 webPreferences 에서 nodeIntegration 을 활성화해야 합니다. 아래를 참조하십시오.

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

electron 5.0 ( Announcement on Repository ) 에서 주요 API 변경 사항이있었습니다 . 최신 버전에서 nodeIntegration 은 기본적으로 false로 설정됩니다 .

문서 Node.js의 Electron 통합으로 인해 모듈, 내보내기, 필요와 같은 DOM에 추가 기호가 삽입되었습니다. 이로 인해 일부 라이브러리에서 동일한 이름의 기호를 삽입하려고하므로 문제가 발생합니다.이를 해결하기 위해 Electron에서 노드 통합을 끌 수 있습니다.

그러나 Node.js 및 Electron API를 사용하는 기능을 유지하려면 다른 라이브러리를 포함하기 전에 페이지에서 심볼의 이름을 변경해야합니다.

<head>
    <script>
        window.nodeRequire = require;
        delete window.require;
        delete window.exports;
        delete window.module;
    </script>
    <script type="text/javascript" src="jquery.js"></script>
</head>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.