Skip to main content

Security best practices: Denial of service

Intermediate
Security
Concept

Protect against draining the cycles balance

Security concern

Canisters pay for their cycles which makes them inherently vulnerable to attacks that consume all their cycles.

Recommendation

  • Consider monitoring, early authentication, and rate limiting on the canister level to mitigate this. Also, be aware that an attacker will aim for the call consuming most cycles. See the "Cycle balance drain attacks section" in how to audit an ICP canister.

  • For query calls that cause significant computation and don't modify the state, it is advisable to not execute the expensive computation if the method is called as an update. However, keep in mind that query calls don't provide authenticity guarantees, so this is a trade-off. Unfortunately, the execution mode of the query (whether it was called as query or update) is currently not directly exposed to the user code. However, one can call a method such as ic0.data_certificate_present() which returns 1 when called as query, and 0 for update methods. See the Interface Specification section on certified data.

  • Expensive calls that only need to be called from other canisters can require some amount of cycles to be sent along with the call to compensate for the cycles consumed by the execution.

  • Finally, it is also an option to charge for ingress messages, but that is not currently supported by the protocol itself and a custom solution would need to be designed.

For expensive calls, consider using captchas or proof of work

Security concern

If an update or query call is expensive in terms of memory used or cycles consumed, this may make it easy for bots to render the canister unusable (e.g. by filling up storage).

Recommendation

If the dapp offers such operations, consider bot prevention techniques such as adding captchas or proof of work. An example captcha implementation can be found in Internet Identity.