Nothing is Truly Private in Smart Contracts
Save your smart contracts from being hacked
Nothing is private in Smart Contracts.
I repeat- NOTHING.
It's a common misconception that variables stored with restricted access control such as "private" cannot be viewed by anyone. Every single piece of data/value that you store in your smart contracts is publicly accessible by anyone in the entire world. This lead's to security vulnerabilities that can result in the complete loss of funds.
Then what's the point of Access Control?
There are 3 types of state visibility in Solidity, namely:
Public
Internal
Private
Let's go over what each of these does:
Public
This provides the least(actually none) access restrictions. This means every function in the current contract as well as any external contract can access the value of this variable.
Internal
This is the 2nd level of access control which imposes few restrictions. Variables defined as internal can only be accessed by the current contract and its subclasses.
Private
This is the highest level of access restriction. However, it doesn't do exactly what its name may imply. Variables declared private can only be accessed by the contract it is defined in. It cannot be accessed by any external contract**.** It is important to note that this states no external contract. If another contract tries to read its value, it results in a compilation error.
However, this does not in any way mean that the value is hidden from the rest of the world. No data on the blockchain is private.
Let's look at how Storage works in the EVM:
Storage Structure in the EVM
The Ethereum Virtual Machine(EVM) stores the state/global variables in a fixed-size array consisting of 2^256 slots. Each of these can hold up to 32 bytes. The default values of these slots are always 0. Hence, you don't need to set the variables' initial value to 0(bonus gas optimization tip *wink wink).
To learn more about Data Locations in the EVM
These variables are stored in the slots according to the order in which they are defined. This means the variable declared first has index 0 and so on. If 2 or more variables combined have a size of 32 bytes, they may be packed together in a single slot as well.
uint8 ==> 1 byte
uint16 ==> 2 bytes
uint64 ==> 8 bytes
uint128 ==> 16 bytes
uint256 ==> 32 bytes
bool ==> 1 byte
address ==> 20 bytes
string ==> 32 bytes
Let us take a sample contract to understand Storage better:
contract example{
uint8 public u8=1;
uint128 private u128=2;
uint256 private u256=10;
bool truth=false;
string private password="nothing is private";
}
Now let's look at what the storage structure looks like for this contract:
We have 3 private variables in the contract. Do you think they are "truly private" and no one can read them? Let's see ๐
Hacking the Contract?
All values in the Storage structure are visible for the world to see, be it public, internal or private. If you are storing sensitive values in your private variables such as the password variable in the above contract, well think again.
All it takes is a simple script in NodeJs to access and read all your storage variables. Although, there are various other ways to do the same as well.
The above contract is deployed on Sepolia Network at address: 0x018D759C779a1c1713Ba5de3e3E552b6e56291A8
const { ethers, utils } = require("ethers");
const rpc_url ="YOUR_NODE_API_URL";
const provider = new ethers.providers.JsonRpcProvider(rpc_url);
async function start() {
const contract_address = "0x018D759C779a1c1713Ba5de3e3E552b6e56291A8";
try {
for (let i = 0; i < 4; i++) {
const data = await provider.getStorageAt(contract_address, i);
if (i == 1) {
console.log(`Data at slot${i} :`, parseInt(data));
continue;
}
if (i == 3) {
console.log(`Data at slot${i} :`, convertHex(data));
continue;
}
console.log(`Data at slot${i} :`, data);
}
} catch (error) {
console.log(error);
}
}
function convertHex(data) {
var hex = data.toString();
var str = "";
for (var n = 0; n < hex.length; n += 2) {
str += String.fromCharCode(parseInt(hex.substr(n, 2), 16));
}
return str;
}
start();
The output received on running this:
Data at slot0 : 0x0000000000000000000000000000000000000000000000000000000000000201
Data at slot1 : 10
Data at slot2 : 0x0000000000000000000000000000000000000000000000000000000000000000
Data at slot3 : nothing is private$
As we can see, your private variable password is not very private anymore.
That's it for this post. I hope now you understand why never save any sensitive information in smart contracts.
For more informative posts, follow me on Twitter
Thank you for reading ๐