Web App Development Security Best Practices
Use a well-audited authentication service and client side IC libraries
Implementing user authentication and canister calls yourself in your web app is error prone and risky. For example, if canister calls are implemented from scratch, there may be bugs around signature creation or verification.
It is of course also an option to consider alternative authentication frameworks on the IC for authentication.
Set an appropriate session timeout
Currently, Internet Identity issues delegations with an expiry time. This expiry time can be set in the auth-client. After a delegation expires, the user has to re-authenticate. Setting a good value is a trade-off between security and usability.
See the OWASP Recommendations. A timeout of 30min should be set for security sensitive applications.
Note: Idle timeouts might be introduced soon, see this.
Don’t use fetchRootKey in agent-js in production
agent.fetchRootKey() can be used in agent-js to fetch the root subnet threshold public key from a status call in test environments. This key is used to verify threshold signatures on certified data received through canister update calls. Using this method in a production web app gives an attacker the option to supply their own public key, invalidating all authenticity guarantees of update responses.
agent.fetchRootKey() in production builds, only in test builds. Not calling this method will result in the hard coded root subnet public key of mainnet being used for signature verification, which is the desired behavior in production.
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.
Validate input in the frontend
Missing input validation of data from untrusted sources (e.g. users) can lead to malformed data being persisted and delivered back to users. This may lead to DoS, injection attacks, phishing, etc.
Perform data validation already in the front end, in addition to data validation in the canister. Data validation should happen as early as possible.
<canister-id>.ic0.app means you completely trust that domain. Also, assets loaded from these domains (incl.
<canister-id>.raw.ic0.app) will not use asset certification.
Note that also loading other assets such as CSS from untrusted domains is a security risk, see e.g. here.
Use a Content Security Policy to prevent scripts and other content from other origins to be loaded at all. See also Define security headers including a Content Security Policy (CSP).
Define security headers including a Content Security Policy (CSP)
Check your site using https://securityheaders.com/
These headers (including CSP) have been successfully integrated e.g. in internet identity.
Check your CSP against CSP evaluator
See also the OWASP secure headers project
Crypto: Protect key material against XSS using Web Crypto API
Use a secure web framework
Modern web frameworks make attacks such as XSS very difficult since they safely escape / sanitize any potentially user-provided data that is rendered on a web page. Not using such a framework is risky as it is hard to avoid attacks like XSS.
Don’t use insecure features of the framework, such as e.g. @html in Svelte.
Make sure the logout is effective
If a logout action by a user is not effective, this may lead to account takeover e.g. if a shared or public device is used.
Make sure also other browser tabs showing the same origin are logged out if the logout is triggered in one tab. This does not happen automatically when agent-js is used, since agent-js keeps the private key in memory once initialized.
Use prompts to warn the user on any security critical actions, let the user explicitly confirm
If this is not the case, a user may by accident execute some sensitive actions.
Show the user a prompt with a security warning describing the exact consequences of the action and let them confirm explicitly.
For applications with high security requirements, consider the use of transaction approval, i.e. using e.g. a WebAuthn device to let the user confirm certain critical actions or transactions. This is recommended e.g. when token or cycle transfers is involved. For example, using a hardware wallet in the NNS dApp achieves this.