{"content":{"title":"每日一学-day004","body":"*最近学习了合约中整型的溢出攻击和签名重放攻击*\r\n\r\n## 整型溢出攻击（Integer Overflow）\r\n\r\n```\r\n原理：\r\nSolidity中uint8，取值范围为0-255，一个数字num = 10，将num = num - 20,那么num = 245,同样上溢也如此。\r\n```\r\n\r\n```\r\ncontract A{\r\n    uint8 public totalSupply;\r\n    mapping(address => uint8) public balanceOf;\r\n\r\n    constructor(uint8 _totalSupply){\r\n        balanceOf[msg.sender] = totalSupply = _totalSupply;\r\n    }\r\n\r\n    function transfer(address to,uint8 amount)external returns(bool){\r\n        unchecked{\r\n        require(balanceOf[msg.sender] - amount >= 0,\"have no enougth token\");\r\n\r\n        balanceOf[msg.sender] -= amount;\r\n        balanceOf[to] += amount;\r\n        }\r\n        return true;\r\n    }\r\n}\r\n注：使用unchecked{},可以使内部的代码会避开整型溢出攻击，否则在好像0.8.0后就会报错。关键代码，balanceOf[msg.sender] - amount >= 0,这段代码无论如何都可以通过，所以会被攻击\r\n```\r\n## 签名重放攻击（Signature Replay）\r\n\r\n```\r\n原理：\r\n1，一笔交易验证过程中，用户对一个msg（假设只包括 to_ 和amount_两个）的hash签名为signature，然后用户发送hash,signature,signer(公钥)去验证。\r\n2，验证方通过hash和signature来恢复出用户的公钥来与signer对比，相同则说明用户是私钥的拥有者。\r\n3，验证通过。然后验证方让用户去mint，第一次用户mint了100个token，接着用户再去mint，由于msg的hash也没有变，所以签名也不会变，验证也会通过，用户又mint了。\r\n\r\n正常的是为我们希望用户mint一次后就不可以mint了，第二次的signature应该跟第一次的signature不相同。造成函数的bug，使得签名可以重放\r\n```\r\n\r\n```\r\n// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.20;\r\n\r\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\r\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\r\nimport \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\r\n\r\ncontract SigReplay is ERC20{\r\n    address public signer;\r\n\r\n    constructor() ERC20(\"Signature\",\"SR\"){\r\n        signer = msg.sender;\r\n    }\r\n\r\n    /* \r\n        一笔交易的签名，可以重复用多次，我们希望的是一个签名用一次\r\n     */\r\n    // function badMint(address to_, uint256 amount_, bytes memory signature_)external{\r\n    //     //获得交易信息\r\n    //     bytes32 hash = getMsgHash(to_,amount_);\r\n    //     //获得交易以太坊签名\r\n    //     bytes32 ethHash = getEthMsgHash(hash);\r\n    //     //验证公钥\r\n    //     require(vertify(ethHash, signature_),\"have no right\");\r\n    //     //mint\r\n    //     _mint(to_,amount_);\r\n    // }\r\n\r\n\r\n    uint public nonce = 0;\r\n    uint256 public blockChainID = 57210;\r\n\r\n    function goodMint(address to_,uint256 amount_,bytes memory signature_)public {\r\n        //获得交易信息\r\n        bytes32 hash = getMsgHash(to_, amount_, nonce, blockChainID);\r\n        //获得交易的以太坊签名\r\n        bytes32 ethHash = getEthMsgHash(hash);\r\n        //验证公钥\r\n        require(vertify(ethHash, signature_));\r\n        //mint\r\n        _mint(to_, amount_);\r\n        nonce++;\r\n    }\r\n\r\n    //5b地址 1000 hash:0xb4a4ba10fbd6886a312ec31c54137f5714ddc0e93274da8746a36d2fa96768be\r\n    //signature:0x5a4f1ad4d8bd6b5582e658087633230d9810a0b7b8afa791e3f94cc38947f6cb1069519caf5bba7b975df29cbfdb4ada355027589a989435bf88e825841452f61b\r\n    function getMsgHash(address to_,uint256 amount_,uint256 nonce_,uint256 blockChainID_)public pure returns(bytes32){\r\n        return keccak256(abi.encodePacked(to_,amount_,nonce_,blockChainID_));\r\n    }\r\n\r\n    function getEthMsgHash(bytes32 hash_)public pure returns(bytes32){\r\n        string memory string_= \"\\x19Ethereum Signed Message:\\n32\";\r\n        return keccak256(abi.encodePacked(string_,hash_));\r\n    }\r\n\r\n    function vertify(bytes32 hash_,bytes memory signature_)public view returns(bool){\r\n        return ECDSA.recover(hash_,signature_) == signer;\r\n    }\r\n}\r\n\r\n注：\r\n这里面的badmint(),发送的消息只需要to_,amount_,如果只是这两个参数进行签名的话，无论调用mint多少次，这里面的ethHash都会跟signature恢复出公钥。相比与goodmint()里面，每调用一次nonce就会改变，让第二次的ethHash跟第一次不相同，虽然signature跟第一次相同，到那时它们两个已经不能生出我们的公钥了。\r\n\r\nnonce可以防止单个合约的签名重放，chainID可以防止不同链之间跨链签名重放攻击。至于使用create2在同一个地址生成合约后在自我销毁的合约，nonce和chainID已经不能阻挡了，这里我思考的是加入一个时间戳或者区块号（由于我还没太熟悉create2，不知道区块号可不可以改变）或其他一些线性的唯一的信息一起hash,是每一笔交易的signature都不相同，即使交易的内容相同，但是交易时的条件也不相同，从而避免签名重放。\r\n```"},"author":{"user":"https://learnblockchain.cn/people/18943","address":"0xb9c0f50035bed4aebfd5e3508c48c9704a3af744"},"history":"bafkreihhs4xhhmj7pb4cixbns27to7khqjixa4twkcr6sftsfsx6q4nch4","timestamp":1712676548,"version":1}