MetaMask launched MetaMask Snaps Open Beta on September 12, 2023. Snaps extend the functionality of the MetaMask extension, which can then be used by connected DApps, for example, to add support for chains, conduct transaction inspections etc.
How MetaMask Snaps Work
First and foremost, the extension of the functionality should not interfere with, or access, user data without special permissions. To achieve a clear distinction between the contexts of the extension and the snap and restricting access to the JavaScript objects such as globalThis, MetaMask uses Secure ECMAScript (SES), developed by Agoric. SES employs an object-capability style in programming as a solution to JavaScript’s supply chain risks, due to which capabilities are dynamically attached to objects. Each snap is thereby run in a secured compartment, which restricts the access of the code run within the compartment to the outside world.
According to the Endo and Hardened JavaScript (SES) Programming Guide, Secure ECMAScript allows for safely running third-party code, which addresses JavaScript’s lack of internal security. Among other things, it also enforces best practices by removing hazardous features. Hence, by utilizing SES, MetaMask can safely allow the running of custom code (snaps), without risking user data or exposure of the runtime context.
Snaps can request specific permissions named ‘endowments,’ to access certain functionalities or APIs. These permissions have to be declared in the manifest file of the snap, and they allow the usage of restricted features or access to restricted resources outside of the snap’s compartment.
From a user’s perspective, the option to install snaps is still an experimental feature, and snaps can be used by installing a special version of MetaMask, Metamask Flask. DApps can request the installation of a snap via the MetaMask extension and can use the functionality provided by installed snaps.
As mentioned, snaps come with a manifest (similar to Android applications or browser extensions). The manifest relays to MetaMask important information about a snap that facilitates interaction and security of the framework, such as the name and description, in addition to the SHA sum, which is used to validate the authenticity of the snap bundle when MetaMask tries to reproduce it during installation. In addition, the manifest needs to state which permissions will be used by the snap; for example, using the dialog option or specific endowments, using an Ethereum provider object to read RPC requests, getting access to the network (which exposes the fetch API), etc. Upon installing a snap, a user will be notified of all the permissions requested by the snap. Thus, documenting why and how those permissions are listed is encouraged to maintain an informed user.
Understanding the execution context of snaps is vital to developing a secure snap. It is important to understand that a snap is still a locally running process in the browser, and any shared resources – that is, usage of levelDB or relying on the operating system’s clock for determining key and session expiry, are not necessarily issues but definitely subjects that should be handled with care. Furthermore, other snaps, extensions, or processes hosted by the operating system could still be manipulated by processes that also have access to them. Additionally, every website could interact with the installed snaps, which leaves users susceptible to a variety of social engineering attacks that could eventually lead to sensitive information being leaked or secrets being hosted by a snap. Since snaps run in a compartmentalized environment, they have no visibility outside of their sandbox except for what is allowed by the listed permissions. By design, snaps cannot access the MetaMask execution environment or its stored data.
Observations in Snap Development
In terms of secure development of snaps, the same principles apply as those in place for secure software application development in general. That means that developers should not try to reinvent the wheel; in other words, external input needs to be validated and authorized, and origins and communication channels should be secure and verified. It also means that the usage of third-party libraries should be actively and properly managed and, at the same time, limited to only necessary cases, as this reduces the possibility of supply-chain attacks.
During audits of multiple snap projects, we came across several recurring problems in the development of snaps. In some cases, projects were failing to utilize the API offered by MetaMask and thereby not benefiting from the security guarantees provided by the execution environment, which could introduce security issues. One example is when the JSON-RPC API is not properly used. Specifically, functionality, which is already provided, is re-implemented by developers. For example, the snap_getentropy function provides snap and account specific deterministic entropy that is ideal for a variety of use cases, such as generating account-coupled keys without risking the exposure of the seed. However, we have seen projects using non-standard libraries to achieve this, resulting in the usage of faulty implementations and exposing the snap to a variety of attacks, such as supply chain attacks.
Another important issue is the granting of excessive privileges. This is a common problem in other areas of application development as well, but we strongly encourage developers to stick to the principle of least privilege (or least authority). This means only requesting the privileges (and endowments) that are strictly needed, and removing permissions from the manifest that are no longer used during the development process.
While conducting snap audits, our team also noticed that it is challenging to adhere to MetaMask snap design guidelines. These guidelines help introduce a snap to end users and keep them informed. We recommend reading the snaps documentation and adhering to design guidelines.
MetaMask lists a few common pitfalls of snap development. These include not choosing correct types of input text to dialogs, not displaying the origin of a DApp in a dialog, not validating an origin for privileged RPC requests, and not validating that privileged origins use https. From a security perspective, all of these are, or can be, relevant. The origin of a request that needs to be authorized by a user should be displayed in the dialog to avoid phishing requests from malicious origins. This is especially critical if the snap expects communication from a specific dApp since snaps can be generally communicated with from all origins by default.
Finally, we encourage snap developers to stay up to date with the latest developments in snaps, as this is still a beta feature in the MetaMask extension and is thereby subject to change. We advise developers to pay attention to changes in the documentation, announcements in official channels, and on official social media accounts, as well as closely monitoring the snaps github repository.
How MetaMask Can Help
Since MetaMask snaps are still in active development, the implementation and specification is subject to change. Oftentimes, only the usage of a new framework reveals the needs of developers. We see some areas in which the design of the snaps ecosystem could be enhanced to aid the developers in producing secure snaps.
Giving the option of white-listing certain domains for custom RPC calls in the build phase by listing them in the manifest would be a valuable feature to ensure a more secure design. Additionally, it could be helpful to give developers the option to mark certain custom RPC calls in the manifest as requiring user consent, thus enforcing keeping an informed user practice.
Developers often make use of external libraries to compute hashes or derive keys. These libraries do not affect the MetaMask extension, but could be faulty or non-secure implementations and thereby make developers rely on unsafe assumptions regarding the security of their snap. It could be beneficial to provide certain cryptographic functions to snaps that then do not need to be re-imported or even re-implemented for each snap.
Another feature that could help developers would be the ability to derive multiple entropies from getEntropy, as well as the ability to derive multiple keypairs from the entropy of RPC calls, such as getBIP44Entropy. A common problem we see developers face is establishing interoperability of identifiers (EOAs). If a set of keys is derived from the entropy provided to snaps, it is specific to the snap, but cannot be easily exported to other software products. Therefore, developers start to develop methods to derive mnemonics from passwords or other information that could then be exported and reused by the user, possibly introducing security vulnerabilities in the process.
Moreover, an endowment could be added that could allow developers to export an encrypted state of the snap. Developers may want to offer this kind of functionality to provide the possibility of creating a secure backup. Since state is already managed by the snaps environment, offering encryption for it could make developing secure snap solutions easier.
To build npm packages, developers use environment files to store API keys or communication endpoints. Since the snaps are installed by pulling the packages from npmjs.com and then installing them, there is no way to provide environment values other than having them be magic values in the code. This is widely considered to be poor practice in other areas of software development, and it would be beneficial to offer a fix for this to encourage developers to implement software development best practices.
The audit submission deadline for inclusion in the MetaMask Snaps Open Beta has passed. However, a publicly available audit report will continue to be a requirement for wallets that implement snaps and desire to be included in the MetaMask snaps directory. For more information about Least Authority security consulting services, please visit our website or schedule a call with us to learn more about the benefits of performing a security audit for projects including MetaMask snaps.