Back to the homepage
Angular

Angular & Electron part 2

In the previous article, we walked through the most crucial aspects of Electron and its security. Now, armed with new knowledge and best practices, we can get down to integrating a sample of an Angular application.

While discussing the communication between renderer and main, we have mentioned several possible ways of doing it, some less and some more recommended.

For didactic reasons, we will integrate our application with Electron by implementing the communication in every possible way. The whole application is available in the public repository.

  1. Application
  2. Remote (ngx-electron)
  3. IPC (ngx-electron)
  4. Preload
  5. PostMessage API
  6. Context Bridge
  7. Local Server
  8. Summary
  9. Useful links

Application

As part of the integration, we will create a sample application using native Electron functionalities.

For demonstration purposes, we will implement the possibility of displaying the native dialog and native notifications informing about the alleged calculations performed on the server-side.

As far as the Electron configuration is concerned, we will use the electron-secure-defaults template mentioned earlier.

Our application looks as follows:

An overview of Electron application using the native features of the platform.

As we already have the skeleton of our application ready, it is time to integrate it with the server part.

Remote (ngx-electron)

First, we need to use the @electron/remote module that is not currently recommended and gives us direct access to the main process from the renderer. As you remember from the previous chapters, this kind of feature can have serious security implications.

We will show you how to configure it, although we do not recommend this approach in practice.

We will use the ngx-electron and @electron/remote packages for this purpose. We execute the command:

Next, we need to initialize the remote module by calling the appropriate method within the initialization of our application, in the main.ts file.

To be able to use this module, we also need to significantly lower the security level of our window by setting selected flags to:

  • nodeIntegration: true
  • context isolation: false
  • enableRemoteModule: true
  • sandbox: false

Moreover, we have to disable the app.enableSandbox() call.

After the initial configuration of ngx-electron, which consists of importing the module and injecting the service, we can use this remote module and call the native functionality from our application.

The code of the service responsible for calling the mentioned functions looks as follows:

Service using a remote module to display dialog and notifications.

IPC (ngx-electron)

We move on to using IPC for communication between the application and our backend. Here we will again use the ngx-electron package and the NestJs controller.

We install our package first:

Again, for us to directly use ipcMain and ipcRenderer we need to set the security-related flags properly:

  • nodeIntegration: true
  • contextIsolation: false
  • sandbox: false

 

We also need to disable the app.enableSandbox() call.

Moving on to the implementation of our backend in NestJs, we define our controller:

A controller that handles specific messages.

The dialog and notification handling are in the AppService. The implementation looks similar to @electron/remote with the difference that we operate directly on Electron modules.

From the application side, implementation comes down again to using service provided by the ngx-electron package, but this time we call ipcRenderer instead of remote.

Service responsible for sending messages to the backend.

This solution seems to be more secure, but still, the attacker can gain unauthorized access to the user’s system because of security features disabled.

Preload

Now we will deal with integration using the preload script. Unlike the above, to make this solution possible, we need to disable a single flag responsible for context isolation:

  • contextIsolation: false

Inside the script itself, we define the API that will be available in the shared window object in our application.

Definition of the preload script that extends the shared window object.

All that’s left is to call the appropriate methods in our service:

Definition of a service calling method on the window object.

To stay true to our conscience, interfaces were created to represent the “new” extended window object.

Interfaces typing the new window object.

As for the controller on the backend, everything remains the same.

In this solution, we used the recommended solution when it comes to communication between Electron processes. However, our application is still not completely immune to vulnerabilities.

PostMessage API

An alternative way to use the preload script is to use the PostMessage API. We then keep the default configuration of our Electron application recommended by electron-secure-defaults.

Moving directly to the implementation, it comes down to modifying the preload script:

The implementation of the preload script using the PostMessage API.

 

And the messaging service:

A service that sends messages to the main via the PostMessage API.

The disadvantage of this solution is that we are unable to communicate synchronously. Also, we have to pass the value “*” as the targetOrigin parameter when sending the message, which itself can cause security vulnerabilities.

So let’s move to a solution that does not require us to compromise on any security level and also provides us with synchronous communication.

Context Bridge

The recommended approach is using the so-called Context Bridge within the preload script to ensure communication between Electron processes.

The implementation again boils down to an appropriate API definition for our application within the preload script, the difference being that we do not extend the window object directly but use the Context Bridge mentioned earlier.

API definition using Context Bridge.

The service that allows us to communicate looks the same as in the case of direct extension of the window object.

This solution is currently one of the safest ways to implement this communication. However, there is another approach that is more flexible and consists of the complete abandonment of communication using IPC.

Local server

The last available solution is to set up a local server. Then, from the level of our application, the communication is carried out using the chosen protocol, just as it was in the case of the remote server.

In this solution, we are free to choose the technology. However, it is important to take into account the solid security of our server.

When it comes to technology, naturally, we use NestJs. So let’s configure our controller:

Controller defining API.

The service implementation remains practically the same except for minimal changes in function signatures (no IPC event).

From the application side, we send requests to the selected address:

Service sending requests to the local server.

The unquestionable advantages of this solution are flexibility in choosing the technology of our backend as well as the possibility of its easy replacement. As we mentioned many times, it is important to properly secure our server.

Summary

Integration of an existing application with Electron is relatively simple, but we must be aware that there are potential vulnerabilities in the security of such applications. However, using all recommended safeguards, our application will certainly be safe.

You can find the source code of our sample application in the repository, and you can verify how the solution works by switching between branches.

We hope you will use the knowledge gained here in practice and create new, safe Electron applications using Angular.

  1. Electron remote module considered harmful, https://nornagon.medium.com/electrons-remote-module-considered-harmful-70d69500f31
  2. Awesome ElectronJs hacking repository, https://github.com/doyensec/awesome-electronjs-hacking
  3. Electronegativity, https://github.com/doyensec/electronegativity
  4. Electron secure defaults, https://github.com/1Password/electron-secure-defaults
  5. Security checklist, https://www.electronjs.org/docs/tutorial/security#checklist-security-recommendations
  6. Sandbox, https://www.electronjs.org/docs/tutorial/sandbox
  7. Session, https://www.electronjs.org/docs/api/session
  8. Electron IPC and NodeIntegration, https://stackoverflow.com/questions/52236641/electron-ipc-and-nodeintegration

About the author

Marcin Leśniczek

Marcin is constantly hungry for knowledge and passionate about mobile and hybrid applications. Always open to new ideas and technologies, being nit-picky. He dedicates his free time to science and astronomy.

Don’t miss anything! Subscribe to our newsletter. Stay up-to-date with the latest trends, tips, meetups, courses and be a part of a thriving community. The job market appreciates community members.

Leave a Reply

Your email address will not be published. Required fields are marked *