Community Conversations | Security Best Practices

Learn how to develop secure dApps on the Internet Computer!

Join our community conversation this Wednesday, November 16th at 7:30 AM PT / 4:30 PM CET. We’ll dive into two aspects of security: storing confidential data by @roel-storms, and issues around inter-canister calls by @robin-kunzler. You’ll learn about interesting and important attack vectors like double spending or loss of confidential data that are relevant in practice, and we’ll discuss how to prevent attacks.

Discussion and questions in this thread are very welcome. We’re looking forward to the conversation!

7 Likes

As promised, we follow up the community conversation with an answer to the questions which were asked during the session. Don’t hesitate to ask additional questions.

The recording will be online soon and a link will be added here.

I tried tagging the people that asked the question if I think I found them on the forum:
@saikatdas0790, @matthewhammer, @thanke

Question 1 by Saikat Das

Is this also applicable for notify calls?

I mean, if I await an inter canister call, the subsequent code is executed as part of a different message.

Does this same behaviour happen for notify calls as well?

notify in ic_cdk::api::call - Rust

(Was answered in the Q&A)

This does not apply to “notify”. As you can see in the source code of the CDK, notify does not return a future as the call function does. Internally, it does not register a callback either:

Question 2 by Saikat Das

Is there a way to put another await after the local balance deduction?

Essentially, what is the right way to do atomic calls across canisters?

(Was answered in the talk and Q&A)

Question 3 by Saikat Das

instead of a lock, we could also put the before balance in a variable and check it after to ensure, it has not changed across the boundary. And if it has, just drop the entire transaction?

(Also answered in the Q&A) Yes, it is possible to detect that a race condition has occurred. However, it will not always be possible to undo inter canister calls that made undesired state changes. In our example, in case of a race condition, we would have transferred ICP in the ledger canister twice. This canister would need an API that allows us to undo this change.

Usually it is easier to prevent the issue than to correct it. However, prevention means that only the first caller would be able to execute and the subsequent calls would receive an error. You could achieve higher throughput in some use cases where an interleaving of messages is not always an issue and it is hard to determine up front if it would be an issue. In that case, letting calls run in parallel could increase throughput and only require a rollback in exceptional cases.

Detecting an issue instead of preventing it could be more challenging in more complex code.

Question 4 by Matthew Harmon

do the canisters get the HTTP headers passed to them?

(was answered in the talk)

HTTP headers are not removed by the boundary nodes and are available to the canisters.

Question 5 by Matthew Harmon

On the subject of encryption, has anything been said about encryption and replay attacks?

(was answered in the talk)

This would not be an issue since the IC protects against replay attacks as stated in the specification: The Internet Computer Interface Specification | Internet Computer Home

Even in case replay attacks were possible on a platform level, you could develop the notes canister API in such a way that the calls are idempotent. For example, replaying the setting of the public key of a device would result in an error since the alias is already registered. Another example would be setting the symmetric encryption key twice would still result in the user intended key being set.

Question 6 by Matthew Harmon

The symmetric key is not derived from the public key, but is encrypted by?

That is correct. If I stated otherwise in the talk, that was a slip of the tongue.

Question 7 by Saikat Das

Are there any suggestions on how to rate limit calls being made to my canister by external callers?

Highly depends on use-case.

  1. There is no load balancer in front of a canister that you can configure as a developer on the IC. Any rate limiting you can influence should be implemented in the canister.

  2. Rate limiting in a canister will also consume cycles. But it may consume less than the actual operation.

  3. The rate limiting strategy depends on the use case. Is this a dApp accessible to anonymous users or are you requiring authentication? In case you have authenticated your users, you could keep track of calls per user per minute and put a limit on that. Returning an error for each call exceeding the limit.

For anonymous users, you could present a captcha for certain operations or as of a certain limit being exceeded.

For expensive calls, you could require users to supply some ICP first as a way to deter attackers.

Question 8 by Timo Hanke

Do we already provide any libraries for this kind of encryption to make it easy for devs?

No, but this would be part of threshold key derivation.

Question 9 by Saikat Das

Any idea if we’re any closer to homomorphic encryption where the hardware takes care of this and devs don’t need to implement secure key exchange themselves?

As they say, the most secure code is the one that isn’t written

  • We are looking at fully-homomorphic encryption (FHE), but is not for the near future.

  • Secure hardware for replicas (AMD SEV-SNP) is actively being looked at, see forum. Hopefully soon, but doesn’t protect against savvy adversary with physical access to machine (see one-glitch attack).

  • vetKD will come with dev libraries for encryption/decryption

Question 10 by Matthew Harmon

curious about the possibilty of an attack using poorly formatted http headers

What do you mean exactly? We’d be interested if you see a concrete attack vector.

3 Likes

It the recording for this out yet? I wasn’t able to make it.

No, not yet. It will be on the DFINITY Youtube Channel once it’s ready. Last I head the team is aiming for the end of today.

1 Like
2 Likes