Update 2016-01-25: This issue has been addressed within the Oraclize it service but this post serves as a write-up on a design pattern that should be avoided by any service.
The abstract solidity contract that Oraclize currently recommends using to integrate with the service exposes contracts to being drained of their entire account balance. This attack is possible because of two key components within the usingOraclize it contract code.
Source Code Here: https://github.com/oraclize/ethereum-api/blob/3a598bc9b119504a12b929116f95296640388b07/oraclizeAPI.sol
First, the code uses a resolver contract. Each time a call is made to the oraclize it service, the contract first queries the resolver contract to find the current address of the Oraclize connector contract.
Second, each query to the oraclize it service calls the getPrice function on the Oraclize it service, and uses the return value of this function as the ether value that is sent to the Oraclize service to pay for future gas costs.
This sets up a situation where your contract is allowing another contract to specify the ether value that it will send with a transaction. Any contract which has used the usingOraclize it contract to integrate with the service, or re-implemented this same logic within their own contract has placed the ether balance of that contract in the hands of the operators of the Oraclize it service.
I'd like to be clear about something.
- I don't believe the Oraclize it operators have any malicious intent.
- I love what Oraclize is doing.
I do however think that it is poor form to encourage a pattern that exposes users to this level of risk.
When thinking about security, it's important to think about the specifics of what you are protecting and who you are protecting it against. In this case, the target is the private key which protects the Ethereum address 0x0047a8033cc6d6ca2ed5044674fd421f44884de8. The thing this key protects is the total account balance of every contract which has implemented this pattern. I don't have figures on hand but it's reasonable to expect this amount could be quite large given a bit of time and some success on Oraclize it's part in getting people to integrate with the service.
If the private key is on Thomas Bertani's personal computer then this paints a large target his back. History has shown that a motivated attacker can penetrate most any system given some time and I expect this is now different. The immutability of contract code exacerbates the situation. The window of time for this attack is effectively infinite. At ANY point in the future if this key is compromised, so are your funds.
The usingOraclize contract needs to be changed to add real protection to the contracts that use it. Here are a few ideas.
- Add bounds checking to the return value of the getPrice function to ensure that it is within reasonable limits.
- Change the resolver contract to require multi-sig to change the address.
- Require users of the usingOraclize contract to implement their own logic for setting the value sent with each query. Provide safe examples on the API docs.
This has been a difficult issue to write about because I've been torn between not wanting to cause bad press for a service that I really like and feeling obligated to report what I see as a significant security flaw in code that people are likely to copy and paste into their contracts.
Update 2016-01-25: I reached out to Thomas Bertani at Oraclize it on Sunday. I wanted to be sure that he both had a chance to address this issue prior to publication as well as to ensure that there was not an error in my assessment. He was very open to discussing the issue and I'm happy to see he's taken steps to address this issue. The usingOraclize it contract now has some basic upper bounds checking on the return value to the getPrice query. He's also informed me that the resolver contract will be transferred to a multi-sig based ownership model.