SWC-136/未加密的链上私人数据
常见的误解是无法读取合约的private类型变量。即使未发布你的合约的源码,攻击者也可以查看 合约交易以确定存储在合约状态中的值。因此,未加密的私有数据不要存储在合约代码或状态中, 这一点很重要。
CWE漏洞分类
整改方案
任何私有数据都应链下存储或仔细加密。
参考文献
示例合约
odd_even.sol
/*
* @source: https://gist.github.com/manojpramesh/336882804402bee8d6b99bea453caadd#file-odd-even-sol
* @author: https://github.com/manojpramesh
* Modified by Kaden Zipfel
*/
pragma solidity ^0.5.0;
contract OddEven {
struct Player {
address addr;
uint number;
}
Player[2] private players;
uint count = 0;
function play(uint number) public payable {
require(msg.value == 1 ether, 'msg.value must be 1 eth');
players[count] = Player(msg.sender, number);
count++;
if (count == 2) selectWinner();
}
function selectWinner() private {
uint n = players[0].number + players[1].number;
(bool success, ) = players[n%2].addr.call.value(address(this).balance)("");
require(success, 'transfer failed');
delete players;
count = 0;
}
}
odd_even.yaml
description: Game contract with private data on-chain
issues:
- id: SWC-136
count: 1
locations:
- bytecode_offsets: {}
line_numbers:
odd_even.sol: [18]
odd_even_fixed.sol
/*
* @source: https://github.com/yahgwai/rps
* @author: Chris Buckland
* Modified by Kaden Zipfel
*/
pragma solidity ^0.5.0;
contract OddEven {
enum Stage {
FirstCommit,
SecondCommit,
FirstReveal,
SecondReveal,
Distribution
}
struct Player {
address addr;
bytes32 commitment;
uint number;
}
Player[2] private players;
Stage public stage = Stage.FirstCommit;
function play(bytes32 commitment) public payable {
// Only run during commit stages
uint playerIndex;
if(stage == Stage.FirstCommit) playerIndex = 0;
else if(stage == Stage.SecondCommit) playerIndex = 1;
else revert("only two players allowed");
// Require proper amount deposited
// 1 ETH as a bet + 1 ETH as a bond
require(msg.value == 2 ether, 'msg.value must be 2 eth');
// Store the commitment
players[playerIndex] = Player(msg.sender, commitment, 0);
// Move to next stage
if(stage == Stage.FirstCommit) stage = Stage.SecondCommit;
else stage = Stage.FirstReveal;
}
function reveal(uint number, bytes32 blindingFactor) public {
// Only run during reveal stages
require(stage == Stage.FirstReveal || stage == Stage.SecondReveal, "wrong stage");
// Find the player index
uint playerIndex;
if(players[0].addr == msg.sender) playerIndex = 0;
else if(players[1].addr == msg.sender) playerIndex = 1;
else revert("unknown player");
// Check the hash to prove the player's honesty
require(keccak256(abi.encodePacked(msg.sender, number, blindingFactor)) == players[playerIndex].commitment, "invalid hash");
// Update player number if correct
players[playerIndex].number = number;
// Move to next stage
if(stage == Stage.FirstReveal) stage = Stage.SecondReveal;
else stage = Stage.Distribution;
}
function distribute() public {
// Only run during distribution stage
require(stage == Stage.Distribution, "wrong stage");
// Find winner
uint n = players[0].number + players[1].number;
// Payout winners winnings and bond
players[n%2].addr.call.value(3 ether)("");
// Payback losers bond
players[(n+1)%2].addr.call.value(1 ether)("");
// Reset the state
delete players;
stage = Stage.FirstCommit;
}
}
odd_even_fixed.yaml
description: Game contract with private data on-chain
issues:
- id: SWC-136
count: 0
locations: []