NNS Proposal: Make canister_status public to anyone

I really think footgun is an overstatement. You don’t need super careful analysis of the exact cycles balances to perform a cycle drain attack. When I look at the costs here https://internetcomputer.org/docs/current/developer-docs/deploy/computation-and-storage-costs/ the most expensive fixed costs are ingress message reception and update call execution. Both of which you know will be subtracted based on the reply of a call.

Then you have ingress byte reception which you also know as an attacker. If somebody accepts Vecs / Text without a limit just make it a 2MB message. And finally you could measure the time to respond knowing that if it takes long there might be Xnet calls involved or larger computations.

I also don’t think people who don’t open source their code would feel the need to set the flag to public. You can’t trust their canister anyways. For the people who do opensource their code the attacker could just try out which function subtracts the most amount of cycles locally.

I suppose this is the main part where we seem to come from different angles. I do not consider exposing private state of the canisters “fairly basic”

Yeah I do guess we have a difference of opinion here and that’s okay. It’s why I wanted to bring it to a vote, because it is not an issue or feature everybody agrees on.

I think the “everything is public” model of Ethereum and other chains has brought some very powerful network effects with it. The ICP ledger certainly wasn’t designed with privacy in mind, but made other tradeoffs.

(sidenote: cycles already have monetary value through dexes like Sonic. Having private ways to store and send cycles could cause issues with regulators in the future because of AML)

I would be interested in knowing what DFINITY would vote though as they have a pretty big chunk of voting power.

The management api is not available through query calls so you have to send ingress messages that are charged to the canister whose status you requested

I didn’t know this and this would kind of devalue the option for making canister_status public. Why is the canister whose status is being requested being charged instead of the canister that initiated the call?

3 Likes

Why is the canister whose status is being requested being charged instead of the canister that initiated the call?

This is the case when the message is initiated by a end-user, not another canister (in the latter the sending canister is charged). Users can also control canisters and if they send management messages like canister_status we need to charge the target canister just like we do for other ingress messages (ingress messages cannot carry cycles so the “target” canister is the only option to apply the charge).

2 Likes

I just wanted to chime in here and mention that making cycles balance public is not merely a footgun but rather a real security issue. That’s because the attacker can learn how many instructions a specific message has executed by reading the cycles balance of the canister before and after the execution. This kind of side-channel attack is known as Timing attack where the time in our case is represented by executed instructions and it is deterministic, making the attack much simpler.

This attack can be used to steal secret/private data from the canister. It is very difficult to protect against the attack in the presence of such a reliable and deterministic side channel. From the security point of view, I would suggest thinking of the public cycles balance as being equivalent to the public canister state. In other words, option D would be safe only for canisters that are fully public and ideally the developers confirm that with “I am okay if everyone can read the canister state” before enabling the flag.

9 Likes

Im glad someone actually brought up a real argument against public cycles.

Making cycles public does not make cycle draining more of a threat in my opinion and anyone who has tested cycle draining on their own canisters locally would realize that pretty quick.

But timing attack to read state would be an insanely complicated attack to pull off even with cycles exposed and that state they are trying to read is already exposed to node operators anyway so canister owners should probably not treat canister state as bulletproof in the first place

1 Like

I don’t think anybody should put any private data at all on the IC. Any node provider in the subnet could access it. I do think this is a more critical issue than a cycle draining attack.

Note though that the official docs currently suggest the blackhole canister as a solution without this warning. Trust in Canisters | Internet Computer Home


Based on the comments from Dimitri and Ulan I have some newly formed thoughts about what I would like to see.

What I need to know as a canister/dev to trust other canisters for critical services

  • The controllers
  • The module hash
  • The rough cycles balance
  • The freezing threshold

(perhaps when subnets become full and busy the memory and compute allocation will become important too)

All this information is essential if I want to have a dependency on another canister in a trustless manner. I do not think services on the IC can be truly composable and interoperable without it. You’ll always have to handle the case where the canister you rely on stop working.

This is not the case on other EVM chains. People can happily build on e.g the uniswap protocol without worrying about the smart contract disappearing.

Why I suggested to make canister_status (optionally) public

canister_status has all this information and already existed. It seemed like the least effort for the foundation and is an API devs are already familiar with.

Why I don’t like the blackhole canister solution

  • It is a bad developer experience needing to manage / keep in mind an extra canister.
  • If you forget it or somebody drains it on purpose you’ll never know your cycles balance again. This could not happen if it is included in the system API.
  • I think it is bad from a security perspective to need to hand over all the permissions that come with being controller just to expose the canister status. What if somebody hacks the Internet Computer documentation page about the blackhole canister and puts their own malicious canister id instead of bxyoi-2iaaa-aaaag-aanhq-cai? It seems the opposite of the capabilities based system Rossberg was a fan of.
  • it doesn’t prevent the issues mentioned by dsarlis and Ulan here.

New suggestion based on the conversation here

A new API that a canister can choose to expose with a flag

  • Exposes the information in canister_status
  • Where cycles are rounded down to the trillion
  • Which can only be called by principals that are able to pay for it themselves

I know @dsarlis said the foundation is already planning to provide B I wonder if this is something that could potentially be added to it.

1 Like

Amen to what @ulan said. After the whole Spectre disaster, from which our industry still hasn’t fully recovered (and probably never will), it would be extremely unwise to remain knowingly shortsighted about these sort of risks.

@Fulco, AFAIK, a controller canister (black hole or otherwise) is not needed to publish cycle balance. If a canister so wishes, nothing prevents it from itself providing a regular method for inquiring its current balance. I would recommend against that, though.

Keep in mind that the ability to store private data safely in canisters is a stated future goal for the IC. Thus the work on using enclaves etc.

7 Likes

I agree 100% with this. I think there are lots of fully public use cases (NFTs for example), but definitely should be used with this in mind.

Having read the follow up responses here I think I now prefer option B.

I think I’ll refrain from making an NNS proposal. The foundation already seems to be building B and exposing cycles balance besides that seems like a more nuanced issue that I don’t think is ready for an NNS vote.

This issue seems more suited for something like a standards committee where different bigger players signal there support. Hopefully ICRC can be used for to decide on a common way to expose cycle balances that don’t give away too much information (e.g by rounding it down to a certain level).

3 Likes

I wanted to drop this not very popular AgentJs feature here.
It currently provides “module_hash”, but doesn’t give “controllers”
I think the documentation says it should The Internet Computer Interface Specification | Internet Computer Home

import { CanisterStatus, HttpAgent } from "@dfinity/agent";

const requestCanisterStatus = (canister_id) => {
  const agent = new HttpAgent({ host: `https://ic0.app` });
  //if (local) await agent.fetchRootKey();
  return CanisterStatus.request({
    canister_id,
    agent,
    paths: ["controllers", "module_hash"],
  });
};
1 Like

@infu I’m not sure what you meant here. Are you saying that there’s some bug in AgentJS? If so, it should be raised as a separate topic (or directly as in issue in the repository). Indeed, you should be able to request both module_hash and controllers from the system state tree.

1 Like

Relevant post that I created sometime back:

This would also make it possible for the canister itself to make that information public with its own public API if it so chooses.

1 Like

Thanks for pointing this out. I’ll reach out to the owners to update the docs. I guess they were not aware of the side-channel attack.

FWIW, a variant of option D that is limited to fully public canisters seems very useful to me. For example, if the flag that the developer would set is named something like “public_canister” or “canister_with_public_state”, then exposing the cycles balance would useful and safe. In that case options B and D are not exclusive to each other and we could support both.

4 Likes

I would love to see a flag like that. While I said I would refrain from making the NNS proposal and that perhaps a standards committee could come together on a way to expose cycles I think just having such a flag is a far superior solution.

We don’t really have a standards committee and these things take a lot of time. I think all of the previous forum threads indicate a demand for such a flag and there are plenty of live use cases today like token or NFT canisters that should use it.

2 Likes

Allowing a canister to query its own status was actually spec’ed out recently and the replica that is being rolled out this week will also include this change. The reasoning being that reading its own status is safe for a canister to do since it’s by definition in its own trust domain.

3 Likes

To get around this type of a Timing/Spectre attack, could the IC introduce a mask or some sort of Cycles cost randomization to the cycles balance returned?

For example: If you are a controlling principal, then you will receive a 100% accurate cycles balance.

However, if you are a not a controller, then you will receive the cycles balance +/- 50,000 cycles (randomized). The basic idea being that users with a vested interest in a canister’s cycles balance will be able to have a general idea of how many cycles a canister has (if it needs to be topped up), but it will take a significant amount more work to extract any useful information due to the cycles mask.

1 Like

I Love to hear this!

@icme, that’s the old idea of weakening the timing signal by injecting noise. Unfortunately, that doesn’t really solve the problem, since it’s usually easy enough for an attacker to strengthen the signal by repeating the probe sufficiently often.

The only measure that really helps is making all code paths equally costly. But that is quite difficult to achieve without special support from the engine. Constant Time Wasm is a research project experimenting with bringing such support to Wasm.

Edit: Somewhat more up-to-date link to CT-Wasm.

10 Likes

@Fulco: I discussed the public canister flag idea with @dsarlis and @Manu. We didn’t immediately see any issues with the idea and its implementation. If you adjust your original proposal to be based on the public canister flag and drive the NNS proposal process, we would be happy to help on the implementation side.

2 Likes

That’s great to hear! I try to write something up.

@wpb @paulyoung @jwiegley @infu @skilesare @blockpunk

As owners of named neurons which a developer background would you support a proposal for option D. Where:

  • canister_status can be made public to be called by anyone with a flag
  • this flag is set to non public by default
  • this flag is called something “public_canister” or “canister_with_public_state” to indicate the implications clearly

If you do please let me know and I’ll include your support in the proposal linking to this forum thread.

5 Likes