Your “private” Solidity Variable is Not “private.” Save it before it becomes Public

One of the most Common but Unheard Issues with Ethereum

If you have worked with Ethereum then you must have written smart contracts, and thus most probably used the keyword private . It seems like it does its work well. Right? Well, actually not.

Anyone in this world with a blockchain explorer can see your so called private variables.

Sounds ridiculous, but yes its true. Let’s see how to hack a contract.

Hacking Smart contracts

Let’s take a vulnerable contract.

It’s basically a game: an odd-even multiplayer game contract, which chooses the winner based on the numbers guessed. Each player chooses a number and if the sum is even then the first player wins, otherwise the second player wins.

The game contract stores the bets of two players in players mapping. Since this variable is declared as private, the second player cannot read the data. Each player has to transfer 1 Ether to the contract in order to play. This condition is checked using require. Once the second player places his bet, the winner is selected based on the odd-even logic and gets the whole bet amount of both players.

Let’s place our bet(execute play(100)). As the number we bet is private so no one can see it. It’s like a secret…

But wait. Let’s see what actually happened when we executed this transaction.

State changes in Ethereum are usually done through a transaction. If the receiving account is a contract in a transaction, the EVM runs the contract’s code either to completion or until the execution runs out of gas.

Details, such as which method to call and input parameters are specified in the data field of each transaction. For example, for modifying a private state variable in the contract, you need to pass the “private” value to the setter method through a transaction. Considering the fact that every transaction data is visible to all the nodes, you could easily read private variables if you know the transaction.

Let’s look into the odd-event contract that we discussed above and see how you can decode the transaction data.

For every method call, the transaction data will have 2 fields:

  1. Method selector
  2. Method parameters

In our smart contract, call to the method will have the following transaction data. Let’s try to decode this.

0x6587f6ec0000000000000000000000000000000000000000000000000000000000000064

First 4 bytes of the transaction data always points to the method signature. It is calculated by taking the first 4 bytes of keccak hash of the method signature. In our example, it is bytes4(keccak256('play(uint)')) which is 0x6587f6ec.

The following characters point to the parameter of the specified method. Each parameter is represented by the hex value of the input padded to 32 bytes. If 100 is the input parameter to play method, then the transaction data for that parameter is:

0x0000000000000000000000000000000000000000000000000000000000000064

BUSTED!

Well, now any one with a block explorer and a bit of knowledge can place his 2nd bet accordingly and rip you off.

This can get more complex if there are more parameters and they are dynamic. You can get more details about argument encoding from the official solidity documentation.

The question now comes is that is there any way to avoid this kind of hack? Well there are some solutions to this:

  • One is to use Quorum instead of Ethereum. It basically allows you to send private transactions. You can specify addresses of the recipients(in this case the address of the contract) in privateFor variable which will only allow the recipients to see the data parameter; while others will receive a blank data parameter. (I have left a lot of details here on how the it works. You can find them here).
  • Another way to tackle this problem while using Ethereum only is using something called commit reveal pattern. In this method, users first submit the hash of a secret information and when everyone else has submitted theirs, each participant reveals their vote, which can be back verified. This is not suited for all the applications, and it adds a lot of complexity on users, but it’s a starting point for further explorations.

read original article here