Lack of access control during upgradable contracts initialization

Hey guys. I’m implementing upgradable contracts using initialize method instead of constructor as defined it here:

However I’ve realized that there are no way to protect initialize method by itself from being called by malicious actor. Is there any way to protect that method by creator only?

While this is a part of deployment and have low chance of probability being abused still it’s an issue.

Best regards

Hey @maqdev! The idea is to call initialize at the same time you deploy the proxy. Note that the Proxy constructor receives a data parameter that is delegatecalled to the implementation contract. This allows you to call initialize in the implementation as the proxy is being constructed, thus preventing anyone else from calling it afterwards, since initializer checks that the method is called only once during the contract’s lifetime.

1 Like

Thanks for explanation! I have 3 contracts mutually depending on each other, so I have to delay initialization, so it’s not possible to call initialize at the same time as deployment. Any way to protect initialization in this scenario?

1 Like

Well, I can think of a few options:

  • If you know the deployment address in advance, you could go as far as hardcoding it in the contract, adding a require(msg.sender == DEPLOYER) in the contract code.
  • If you are not using the OZ CLI (or any CLI for that matter) for the deployment, you could write a small factory contract with a deployEverything method that creates all three proxies and then initializes them. Since this would all happen in the same tx, there is no risk of someone calling the initializer before you.
  • If you are feeling particularly hacky, you could take advantage of the fact that deployment addresses can be known in advance (they are a hash of the deployer account and an increasing nonce), precompute them, and initialize the proxies using addresses that still have not been deployed.
  • Just ignore the problem. If someone does manage to call initialize before you do, then the initialize transaction would just fail, in which case you just discard the current set of proxies and deploy a new set.

Unless you expect some particularly nasty people being after you, I’d go with the last one.