This document lists typical steps of a security review needed for production-ready IC dapps. We indicate whether the two backend implementations of Encrypted Notes comply with the corresponding requirements (marked as Done), do not yet comply (Future), or whether a particular requirement is not applicable to this backend (Not applicable).
While this list might help creating better IC dapps, keep in mind that the list is potentially incomplete. In particular, each real-world dapp may have a different set of security requirements that depend on its target domain and intended use case.
- Motoko: Done
- Rust: Done
- Motoko: Done
- Rust: Done
Avoid using uncertified queries in public canister APIs. Instead, either use certified update methods or design an eventual certification approach for performance-critical dapps.
- Motoko: Done (no query methods)
- Rust: Done (no query methods)
Each public API method should sanitize their arguments and gracefully handle exceptional situations.
- Motoko: Done
- Rust: Done
- Motoko: Done
- Rust: Done
For example, the initialization vector for AES-GCM encryption should be unique for each message (or chosen at random).
- Motoko: Done
- Rust: Done
- Motoko: Done
- Rust: Done
When generating the private key using crypto.subtle.generateKey, set extractable=false. Consider offloading the secret keys to a YubiKey or YubyHSM so that the secret keys never end up in the browser.
- Motoko: Done
- Rust: Done
- Motoko: Future
- Rust: Future
For example, a security-sensitive dapp like Encrypted Notes should set maxTimeToLive for Internet Identity delegation to 30 min rather than 24 h.
- Motoko: Future
- Rust: Future
- Motoko: Future
- Rust: Future
- Motoko: Done
- Rust: Done
- Motoko: Not applicable
- Rust: Done
- Motoko: Done
- Rust: Done
- Motoko: Done (except versioning)
- Rust: Done (except versioning)
- Motoko: Done
- Rust: Done
- Motoko: Future
- Rust: Future
- Motoko: Done (we don't use await)
- Rust: Done (we don't use await)
- Motoko: Done (we have no inter-canister calls)
- Rust: Done (we have no inter-canister calls)
- Motoko: Done (we have no inter-canister calls)
- Rust: Done (we have no inter-canister calls)
- Motoko: Done
- Rust: Done
- Motoko: Done, assuming that
Iter.toArrayandMap.fromIterdo not trap. - Rust: Done, assuming that
borrow_mut,std::mem::take, andic_cdk::storage::stable_savedo not panic.
If the canister storage becomes too big, the canister will no longer be upgradable because pre_upgrade will time out or the canister will run out of cycles. The recommended remedy is to use stable memory directly rather than serializing data upon upgrade.
- Motoko: Future
- Rust: Future
- Rust: Done
- Rust: Done
- Motoko: Future
- Rust: Future
- Motoko: Future
- Rust: Future
- Motoko: Done (via Docker)
- Rust: Done (via Docker)
- Motoko: Future
- Rust: Future
- Motoko: Done
- Rust: Done
- Motoko: Future
- Rust: Future
- Adding submit_ciphertexts is currently O(C*D) where
C = ciphertexts.size()andD = store.device_list.size()
12.1. Confirm user's intention before executing potentially irreversible actions like device removal
- Motoko: Future
- Rust: Future
- Motoko: Future
- Rust: Future