General security best practices
Overview
This document provides security best practices for developing canisters and web apps served by canisters on the BigFile. These best practices are mostly inspired by issues found in security reviews.
The goal of these best practices is to enable developers to identify and address potential issues early during development of new dapps, and not only in the end when (if at all) a security review is done. Ideally, this will make the development of secure dapps more efficient.
Some excellent canister best practices linked here are from effective Rust canisters and how to audit an BigFile canister. The relevant sections are linked in the individual best practices.
Target audience
This document was initially intended for internal use at BigFile. However, it has now been published to the community so every developer can benefit. The target audience are any developers working on canisters or web apps for the BigFile. This is also of interest to anyone doing reviews of such code.
Disclaimers and limitations
The collection of best practices may grow over time. While it is useful to improve security of dapps on the BigFile, such a list will never be complete and will never cover all potential security concerns. For example, there will always be attack vectors very specific to a dapps use cases that cannot be covered by general best practices. Thus, following the best practices can complement, but not replace security reviews. Especially for security critical dapps it is recommended to perform security reviews/audits. Furthermore, please note that the best practices are currently not ordered according to risk or priority.
Certify query responses if they are relevant for security
Security concern
The responses to query calls (as opposed to update calls) are not threshold-signed by the canister/subnet. Thus, a single malicious replica or boundary node may change the data, violating its authenticity. This is especially risky if update calls depend on the response to query calls.
Recommendation
All security-relevant query response data that needs authenticity guarantees (this needs to be assessed for each dapp) should be certified by BIG using certified variables. Consider using existing data structures such as certified-map. The data certification must be validated in the frontend.
Alternatively, these calls have to be issued as update calls by the caller (e.g. in agent-js), but that impacts performance: it takes a few seconds. Note that every query can also be issued as an update by the caller.
Examples are asset certification in Internet Identity, NNS dapp, or the canister signature implementation in Internet Identity.
Data confidentiality on the BigFile
Security concern
When storing data on the BigFile, there are two levels of data access.
Nodes are able to read all data that is stored on a subnet. This includes all messages sent to or from a canister, along with all data stored in a canister. This means a node could extract all data available to a canister. This will change with the implementation of TEE-based security for nodes.
End user clients can only access whatever data that nodes and canisters have made available to them. If the subnet's nodes do not misbehave and leak data, clients can only read the responses to ingress messages and queries that they have sent. The canister decides what data is exposed to the client.
Partial information on data that is stored in the subnet state tree will always leak. Therefore, data with a low-entropy value may entirely leak and be fully exposed, such as a Boolean value that can only be either "True" or "False". Leakage on data with a high-entropy is negligible.
There are two types of user-related data that may be stored in the subnet state tree. The first is when a user sends an ingress message to a canister, the message hash and the response are both stored in the subnet state tree to be retrieved securely by the client. The ingress message should contain a high-entropy nonce that is implemented by the agent and typically not exposed to the user. The message response is determined by the canister and may not contain a high-entropy value. If the canister response consists of a low-entropy value, then the data may be leaked to users other than the ingress message sender.
The second type of user-related data is certified variables maintained by a canister that are also exposed through the subnet state tree. If a canister places low-entropy data into the state tree, then the data may leak to users who should not have access to that piece of data.
Recommendation
For developers that need to protect the confidentiality of their data against external users, they should ensure that data in the subnet state tree has a sufficient level of entropy. 128 bits is recommended. If the data does not have enough entropy itself, then adding some artificial data using randomness would be recommended.
In particular, a canister can ensure that responses to ingress messages do not leak data to external users, other than the sender, by including high-entropy data in the response. Or, a canister can ensure that data in certified variables is not leaked by adding high-entropy data to the variables that should be kept confidential.
Additionally, similarly to ingress message responses, a canister's private custom sections that contain low-entropy data could leak to unauthorized users. Therefore, a sufficent level of entropy for canister private custom sections should be used. 128 bits is recommended. If the data does not have enough entropy itself, then adding some artificial data using randomness would be recommended.