Skip to main content

General Best Practices

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 the IC 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.

Nonspecific to the Internet Computer

The best practices in this section are very general and not specific to the Internet Computer. This list is by no means complete and only lists a few very specific concerns that have led to issues in the past.

Don’t use third-party components with known vulnerabilities

Security Concern

Using vulnerable and outdated components is a big security risk.

Recommendation

  • Regularly check your third party components against databases of known vulnerabilities:

  • Rust: use cargo audit.

  • JavaScript / NPM: use npm audit

  • This should be integrated into the build process, the build should fail if there are known vulnerabilities.

  • Don’t use forked versions of repositories that are not maintained and may not be trustworthy.

  • Avoid using third party components that are not widely used and may not have had sufficient (ideally third party) review.

  • Pin the versions of the components you are using to avoid switching to corrupt updates automatically.

Don’t implement crypto yourself

Security Concern

It is easy to make mistakes when implementing cryptographic algorithms, leading to security bugs.

Recommendation

  • Use well known libraries that may be open source and have been reviewed by many people. For example, use the Web Crypto API in JavaScript, use crates such as sha256 in Rust

Use secure cryptographic schemes

Security Concern

Some cryptographic schemes have been broken (old TLS versions, MD5, SHA1, DES, …​), or they could be so new that they have not yet been appropriately researched. Using these introduces security issues.

Recommendation

If you need to use crypto, only use cryptographic schemes that have not been broken and do not have known issues. Ideally use algorithms that have been standardized by e.g. NIST or IETF.

References:

Test your code

Security Concern

Having small test coverage is risky, as code changes become difficult and may violate correctness and security properties, leading to bugs. It is hard to verify correctness and security properties in reviews (and security reviews) if there are no corresponding tests.

Recommendation

Write tests for canister implementations and frontend code, especially for security relevant properties and invariants.

Avoid test and dev code in production

Security Concern

It is risky to include code paths in production code that are only used for development or testing setups. If something goes wrong (and it sometimes does!), this may introduce security bugs in production.

For example, we have seen issues where the public key to verify certification was fetched from an untrusted source, since this is what is done on test networks.

Recommendation

Avoid test and dev code in production code whenever possible.