Back to the homepage
Angular

Angular & Electron part 1

Like we have already shown on our blog, Angular is not necessarily limited to web applications (PWA, Capacitor). Today we will try to run our application directly on the desktop, and for this, we will use the Electron framework.

In this article, we will present you with a holistic view of this type of application. In the next part, we will guide you through the process of integrating a full-stack application and show you how to use the capabilities of NestJS in the Electron environment. Also, we will analyze the widely discussed security aspects of this type of application.

Let’s start by discussing Electron itself.

  1. What is Electron?
  2. Architecture
    2.1. Remote
    2.2. IPC
    2.3. ngx-electron
    2.4. Preload
    2.5. PostMessage API
    2.6. Context Bridge
    2.7. Local Server
  3. Security
    3.1. Security checklist
    3.2. Electronegativity
    3.3. Electron hardener
  4. Examples of incorrect configuration
    4.1. Node Integration
    4.2. Context Isolation
    4.3. Sandboxing
  5. Summary
  6. Useful links

What is Electron?

In a few words, it is a framework developed by GitHub developers allowing to create native applications for Windows, Linux, or macOS using web technologies. In practice, it is a combination of Chromium, which is responsible for launching our application, and NodeJs, which allows us to perform native operations.

Transforming an existing application into an Electron application is child’s play and comes down to a few steps, which I will describe in detail in the next article about integration.

Due to the nature of the framework, we are once again dealing with cross-platform programming using a single, underlying codebase.

Architecture

Electron applications are based on two fundamental concepts. I’m talking about renderer process, corresponding to a particular application windows, and about the main process, operating in the NodeJs environment, with access to native environment and  functionalities.

Schematic representation of the Electron application architecture.

However, to realize the potential of our application’s architecture, we must somehow implement communication between these two separate processes. There are several approaches available.

Remote

In the beginning, when Electron was still in its infancy, communication between the renderer and the main process was done using the remote module, which allowed us to use NodeJS modules directly from the application.

This solution was problematic for security and performance reasons and was finally marked as deprecated. You can read more about why this is the case in Jeremy Rose’s article.

The documentation for the remote module has been completely deleted, but the authors have previously suggested using IPC. So how should we implement our communication?

IPC

Inter-Process Communication is a mechanism that allows communication between different processes within a single operating system. In the case of Electron, it is used to send synchronous or asynchronous messages between the renderer and the main (also within the Electron implementation itself).

Electron provides us with ipcMain and ipcRenderer modules, through which we send and listen for messages. However, to be able to use these modules directly, the application must have the nodeIntegration flag enabled.

Unfortunately, this has quite far-reaching consequences in case of a vulnerability in the application as an XSS attack may even turn into an RCE. The security issue related to this flag will be discussed in more detail in the Security chapter.

ngx-electron

In case of Angular applications, we have a package defining services for the mentioned communication. This package uses IPC modules underneath so our application must also have the nodeIntegration flag enabled.

Thanks to this package, we have access to the service, through which we can send messages to the main. In addition, we also have a few different APIs provided by Electron. The descriptions of all available options can be found in the documentation.

Preload

When launching our application and creating its main window, we can call the so-called preload script. We have access to both the window object from the script and the NodeJs environment.

A popular technique was to extend the window object within the mentioned script with additional methods to perform operations in the NodeJs environment. It is possible because the renderer and the main share the same context.

We can use this solution with the nodeIntegration flag disabled, taking care of our application security at the very beginning.

In the configuration of our window, we can also set the contextIsolation flag (which we will talk about in the Security section). This flag must be disabled to use this technique.

As you may have guessed, this type of solution is also questionable in terms of security. As with IPC, XSS-type attacks can turn into RCEs, which we will talk more about in the Security section.

PostMessage API

To implement communication between the main and the renderer we can also use the PostMessage API.

As in the case of preload, we can also use it with the nodeIntegration flag turned off.

The implementation comes down to the definition of listening methods for messages within the preload script and within the application itself.

Unfortunately, according to the documentation, this protocol does not support sources of type “file://“, so when sending a message we have to pass the value “*” (asterix) as a parameter for targetOrigin, which in itself may have consequences within general security.

Context Bridge

Each of the solutions outlined above required us to make some compromises regarding the security of our application. Finally, we have a solution that is currently recommended for communication between the main and renderer processes.

As we mentioned earlier, enabling the contextIsolation flag causes the window objects in both processes to be different, and expanding them doesn’t make any sense.

However, to make it possible, Electron provides a so-called Context Bridge, through which we can safely define a kind of API for our application, which in turn we can use in the renderer.

Local server

As we run our application in the NodeJs environment, nothing prevents us from starting a local server at this moment, with which our application will communicate using the selected protocol.

This solution allows us to use any available framework to implement our backend.

In our article, as Angular is our favorite, we will use its server-side equivalent – NestJs.

In the case of this solution, we have to keep in mind that the server must have its well-configured security. We can therefore conclude that the security of our application depends largely on the level of security of the locally running server.

We use all of the above solutions when integrating our sample application with Electron.

But before we begin, let’s focus for a moment more on Electron application security and why it is so important.

Security

Despite the advantages of using Electron, the security of this type of application is commonly criticized. It is most often due to improper configuration, allowing attacks such as XSS (Cross-site scripting) or even RCE (Remote Code Execution).

The latter is possible due to the nature of Electron application architecture. As the renderer has access to the main, it is also possible to access the operating system from its level. Then once the attacker somehow gains the ability to invoke malicious code on the renderer side, they can escalate the scope of that attack to the victim system

Virtually every major Electron application has been vulnerable to this type of attack for some time, including the Steam client, WhatsApp, Discord, Slack, Teams, or even VSCode.

A great source for all aspects of security and attacks is the awesome-electronjs-hacking repository, collecting various lectures, videos, and articles on the subject in one place.

Do you recognize all the Electron applications shown?

The security issue is furthermore important due to the ever-increasing number of Electron applications, with as many as 950 officially available at the time of writing this article.

So how to meet the high-security standards?

Security checklist

To reduce the number of incorrectly configured applications and improve their security, Electron developers have created a list of recommended practices and settings.

Security is addressed in a separate section of the documentation that explains the recommended settings in detail.

Electronegativity

As part of preventing incorrect configurations, there is also an Electronegativity tool available. It is an npm package that verifies our application against the recommended security features and provides documentation discussing the issues found.

A great tool to verify the security of our application.

Electron secure defaults

The developers at 1Password created a template for secure Electron applications when developing their applications.

It is a great place to start your adventure with creating this type of application, keeping in mind the highest possible level of security.

Electron hardener

Electron hardener is another tool created by people from 1Password. It was designed to disable any additional flags with which the Electron application can be run which in the end could lead to possible holes.

If we want our application to be considered safe, we should use this library.

Examples of incorrect configuration

Node Integration

One of the main goals of Electron was to combine the capabilities of web applications with NodeJs within a single application. To this end, developers were given access to the native environment from within the renderer.

The idea itself is perfectly valid. Unfortunately, this approach makes it so that any security vulnerabilities can lead to RCE. There is a possibility to disable direct communication between renderer and main.

We are talking about the nodeIntegration flag, which is set when creating a window since v5 is disabled by default for newly created views.

However, there is a question of how Electron can serve as a desktop application without access to the NodeJs environment? As we mentioned earlier, many alternative approaches have since emerged that allow communication between processes in a secure manner, such as Context Bridge,

Example exploitation of a vulnerability related to this flag: the Notable application.

Context Isolation

The preload script we talked about earlier and the renderer process shared a context by default for a long time, i.e. changes to the window object within one process were visible in the other.

Electron provides us with the contextIsolation flag, which we can use to remove this functionality, and since v12 it is enabled by default. When Context Isolation is enabled, each renderer process and preload script have the copies of the window, document, etc. objects they operate on.

The sharing of context between these processes could be exploited to launch successful XSS attacks via prototype overwriting for instance.

Examples of vulnerability exploitation are Discord application or WireApp.

Sandboxing

As Electron uses Chromium to render the web application, it is possible to take advantage of additional security features included in Chromium itself.

One of them is sandboxing, i.e. limiting privileges of a given process so that even in case of a successful attack, the attacker will not be able to escalate his privileges and cause damage to the victim’s system.

Since version v5, the sandbox flag is enabled by default.

Summary

We presented you with a technology that allows creating applications that run on desktops, even without access to the network. And all this in our favorite Angular!

However, we must be aware that there are potential security vulnerabilities in this type of application. But if we apply the recommended approaches and all the precautions, I think we can confidently say that such an application will be safe.

Hopefully, after reading this article, you will test your new knowledge and create your secure desktop 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 *