यह लेख स्मार्ट कॉन्ट्रैक्ट सुरक्षा पर एक मिनी-कोर्स के रूप में कार्य करता है और उन मुद्दों और कमजोरियों की एक विस्तृत सूची प्रदान करता है जो सॉलिडिटी स्मार्ट कॉन्ट्रैक्ट्स में पुनरावृत्ति करते हैं। ये ऐसे मुद्दे हैं जो गुणवत्ता लेखापरीक्षा में सामने आ सकते हैं।
सॉलिडिटी में एक सुरक्षा समस्या स्मार्ट कॉन्ट्रैक्ट्स के लिए उबलती है, जिस तरह से उनका इरादा था वैसा व्यवहार नहीं कर रहा है।
धन की चोरी हो रही है
किसी अनुबंध के अंदर निधियों का बंद होना या जम जाना
लोगों को अनुमान से कम पुरस्कार मिलते हैं (पुरस्कार विलंबित या कम हो जाते हैं)
लोग प्रत्याशित से अधिक पुरस्कार प्राप्त करते हैं (मुद्रास्फीति और अवमूल्यन के लिए अग्रणी)
हर उस चीज की विस्तृत सूची बनाना संभव नहीं है जो गलत हो सकती है। हालाँकि, जिस तरह पारंपरिक सॉफ्टवेयर इंजीनियरिंग में कमजोरियों के सामान्य विषय होते हैं जैसे कि SQL इंजेक्शन, बफर ओवररन और क्रॉस साइट स्क्रिप्टिंग, स्मार्ट कॉन्ट्रैक्ट्स में आवर्ती एंटी-पैटर्न होते हैं जिन्हें प्रलेखित किया जा सकता है।
इस गाइड को एक संदर्भ के रूप में अधिक सोचें। इसे पुस्तक में बदले बिना हर अवधारणा पर विस्तार से चर्चा करना संभव नहीं है (निष्पक्ष चेतावनी: यह लेख 10k + शब्द लंबा है, इसलिए इसे बुकमार्क करने के लिए स्वतंत्र महसूस करें और इसे विखंडू में पढ़ें)। हालाँकि, यह इस बात की सूची के रूप में कार्य करता है कि क्या देखना है और क्या अध्ययन करना है। यदि कोई विषय अपरिचित महसूस करता है, तो उसे एक संकेतक के रूप में काम करना चाहिए कि भेद्यता के उस वर्ग की पहचान करने के अभ्यास में समय देना उचित है।
यह लेख सॉलिडिटी में बुनियादी दक्षता को मानता है। यदि आप सॉलिडिटी के लिए नए हैं, तो कृपया हमारा निःशुल्क सॉलिडिटी ट्यूटोरियल देखें।
हमने स्मार्ट कॉन्ट्रैक्ट रीएंट्रेंसी पर विस्तार से लिखा है, इसलिए हम इसे यहां नहीं दोहराएंगे। लेकिन यहाँ एक त्वरित सारांश है:
जब भी एक स्मार्ट अनुबंध दूसरे स्मार्ट अनुबंध के कार्य को कॉल करता है, उसे ईथर भेजता है, या उसमें एक टोकन स्थानांतरित करता है, तो फिर से प्रवेश की संभावना होती है।
रीएन्ट्रेंसी संभवतः सबसे प्रसिद्ध स्मार्ट अनुबंध भेद्यता होने के बावजूद, यह जंगली में होने वाले हैक्स का केवल एक छोटा प्रतिशत बनाता है। सुरक्षा शोधकर्ता Pascal Caversaccio (pcaveraccio) जीथब पर पुनर्प्रवेश आक्रमणों की अद्यतन सूची रखता है। अप्रैल 2023 तक, उस रिपॉजिटरी में 46 रीएंट्रेंसी हमलों का दस्तावेजीकरण किया गया है।
यह एक साधारण गलती की तरह लगता है, लेकिन एक संवेदनशील कार्य (जैसे ईथर को वापस लेना या स्वामित्व बदलना) पर प्रतिबंध लगाना भूल जाना आश्चर्यजनक रूप से अक्सर होता है।
यहां तक कि अगर एक संशोधक जगह में है, तो ऐसे मामले सामने आए हैं जहां संशोधक को सही ढंग से लागू नहीं किया गया था, जैसे कि नीचे दिए गए उदाहरण में जहां आवश्यकता कथन गायब है।
// DO NOT USE! modifier onlyMinter { minters[msg.sender] == true_; }
यह उपरोक्त कोड इस ऑडिट से एक वास्तविक उदाहरण है: https://code4rena.com/reports/2023-01-rabbithole/#h-01-bad-implementation-in-minter-access-control-for-rabbitholereceipt-and- rabbitholetickets-contract
यहाँ एक और तरीका है जिससे अभिगम नियंत्रण गलत हो सकता है
function claimAirdrop(bytes32 calldata proof[]) { bool verified = MerkleProof.verifyCalldata(proof, merkleRoot, keccak256(abi.encode(msg.sender))); require(verified, "not verified"); require(alreadyClaimed[msg.sender], "already claimed"); _transfer(msg.sender, AIRDROP_AMOUNT); }
इस मामले में, "पहले से ही दावा किया गया" कभी भी सत्य पर सेट नहीं होता है, इसलिए दावेदार कई बार फ़ंक्शन को कॉल कर सकता है।
अपर्याप्त अभिगम नियंत्रण का एक हालिया उदाहरण एक ट्रेडिंग बॉट द्वारा फ्लैशलोन प्राप्त करने के लिए एक असुरक्षित कार्य था (जो 0xbad नाम से जाना जाता था, क्योंकि पता उस क्रम से शुरू हुआ था)। इसने एक मिलियन डॉलर से अधिक का लाभ कमाया जब तक कि एक हमलावर ने यह नहीं देखा कि कोई भी पता फ्लैशलोन रिसीव फंक्शन को कॉल कर सकता है, न कि केवल फ्लैशलोन प्रदाता को।
जैसा कि आमतौर पर ट्रेडिंग बॉट्स के साथ होता है, ट्रेडों को निष्पादित करने के लिए स्मार्ट कॉन्ट्रैक्ट कोड को सत्यापित नहीं किया गया था, लेकिन हमलावर ने वैसे भी कमजोरी का पता लगाया। अधिक जानकारी रेक्ट न्यूज कवरेज में।
यदि अभिगम नियंत्रण किसी फ़ंक्शन को कॉल करने वाले को नियंत्रित करने के बारे में है, तो इनपुट सत्यापन यह नियंत्रित करने के बारे में है कि वे अनुबंध को क्या कहते हैं।
यह आमतौर पर उचित आवश्यकता बयानों को जगह में रखने के लिए भूल जाता है।
यहाँ एक प्राथमिक उदाहरण है:
contract UnsafeBank { mapping(address => uint256) public balances; // allow depositing on other's behalf function deposit(address for) public payable { balances += msg.value; } function withdraw(address from, uint256 amount) public { require(balances[from] <= amount, "insufficient balance"); balances[from] -= amount; msg.sender.call{value: amout}(""); } }
उपरोक्त अनुबंध यह जाँचता है कि आप अपने खाते से अधिक राशि नहीं निकाल रहे हैं, लेकिन यह आपको मनमाने खाते से निकासी करने से नहीं रोकता है।
Sushiswap ने इस प्रकार के एक हैक का अनुभव किया, क्योंकि बाहरी फ़ंक्शन के एक पैरामीटर को साफ नहीं किया जा रहा था।
अनुचित अभिगम नियंत्रण का अर्थ है कि संदेश भेजने वाले के पास पर्याप्त प्रतिबंध नहीं हैं। अनुचित इनपुट सत्यापन का अर्थ है कि फ़ंक्शन के तर्क पर्याप्त रूप से स्वच्छ नहीं हैं। इस विरोधी प्रतिमान का एक व्युत्क्रम भी है: फ़ंक्शन कॉल पर बहुत अधिक प्रतिबंध लगाना।
अत्यधिक सत्यापन का मतलब शायद धन की चोरी नहीं होगी, लेकिन इसका मतलब यह हो सकता है कि धन अनुबंध में बंद हो जाए। बहुत अधिक सुरक्षा उपायों का होना भी अच्छी बात नहीं है।
सबसे हाई-प्रोफाइल घटनाओं में से एक अकुटर्स एनएफटी थी, जो 34 मिलियन डॉलर मूल्य के एथ के साथ स्मार्ट अनुबंध के अंदर फंस गई और वापस नहीं ली जा सकी।
अनुबंध के मालिक को डच नीलामी मूल्य से ऊपर भुगतान करने से सभी रिफंड दिए जाने तक अनुबंध के मालिक को वापस लेने से रोकने के लिए एक सुविचारित तंत्र था। लेकिन नीचे लिंक किए गए ट्विटर थ्रेड में दर्ज बग के कारण, मालिक फंड निकालने में असमर्थ था।
Sushiswap ने अविश्वसनीय उपयोगकर्ताओं को बहुत अधिक शक्ति प्रदान की, और Akutars NFT ने व्यवस्थापक को बहुत कम शक्ति प्रदान की। स्मार्ट अनुबंधों को डिजाइन करते समय, प्रत्येक वर्ग के उपयोगकर्ताओं को कितनी स्वतंत्रता दी जानी चाहिए, इस बारे में एक व्यक्तिपरक निर्णय, और यह निर्णय स्वचालित परीक्षण और टूलिंग के लिए नहीं छोड़ा जा सकता है। विकेंद्रीकरण, सुरक्षा और यूएक्स के साथ महत्वपूर्ण ट्रेडऑफ़ हैं जिन पर विचार किया जाना चाहिए।
स्मार्ट कॉन्ट्रैक्ट प्रोग्रामर के लिए, स्पष्ट रूप से लिखना कि उपयोगकर्ताओं को कुछ कार्यों के साथ क्या करना चाहिए और क्या नहीं करना चाहिए, यह विकास प्रक्रिया का एक महत्वपूर्ण हिस्सा है।
हम दबंग व्यवस्थापकों के विषय पर बाद में फिर से विचार करेंगे।
जैसा कि परिचय में कहा गया है, स्मार्ट कॉन्ट्रैक्ट हैक होने के चार प्राथमिक तरीके हैं:
यहां "मनी" का अर्थ कुछ भी मूल्यवान है, जैसे कि टोकन, न कि केवल क्रिप्टोक्यूरेंसी। किसी स्मार्ट कॉन्ट्रैक्ट की कोडिंग या ऑडिट करते समय, डेवलपर को इस बात के प्रति ईमानदार होना चाहिए कि मूल्य अनुबंध के अंदर और बाहर प्रवाहित होता है। ऊपर सूचीबद्ध मुद्दे स्मार्ट अनुबंधों को हैक करने के प्राथमिक तरीके हैं, लेकिन ऐसे कई अन्य मूल कारण हैं जो प्रमुख मुद्दों में कैस्केड कर सकते हैं, जिन्हें नीचे प्रलेखित किया गया है।
वोट तौलने के लिए टिकट के रूप में वैनिला ERC20 टोकन या NFT का उपयोग करना असुरक्षित है क्योंकि हमलावर एक पते से मतदान कर सकते हैं, टोकन को दूसरे पते पर स्थानांतरित कर सकते हैं, और उस पते से फिर से मतदान कर सकते हैं।
यहाँ एक न्यूनतम उदाहरण है:
// A malicious voter can simply transfer their tokens to // another address and vote again. contract UnsafeBallot { uint256 public proposal1VoteCount; uint256 public proposal2VoteCount; IERC20 immutable private governanceToken; constructor(IERC20 _governanceToken) { governanceToken = _governanceToken; } function voteFor1() external notAlreadyVoted { proposal1VoteCount += governanceToken.balanceOf(msg.sender); } function voteFor2() external notAlreadyVoted { proposal2VoteCount += governanceToken.balanceOf(msg.sender); } // prevent the same address from voting twice, // however the attacker can simply // transfer to a new address modifier notAlreadyVoted { require(!alreadyVoted[msg.sender], "already voted"); _; alreadyVoted[msg.sender] = true; } }
इस हमले को रोकने के लिए ERC20 स्नैपशॉट या ERC20 वोट का इस्तेमाल किया जाना चाहिए। भूतकाल में किसी समय को स्नैपशॉट करके, अवैध मतदान शक्ति प्राप्त करने के लिए वर्तमान टोकन शेष राशि में हेरफेर नहीं किया जा सकता है।
हालांकि, स्नैपशॉट या वोट क्षमता के साथ ERC20 टोकन का उपयोग करने से समस्या का पूरी तरह से समाधान नहीं होता है यदि कोई अस्थायी रूप से अपना बैलेंस बढ़ाने के लिए फ्लैशलोन ले सकता है, तो उसी लेनदेन में अपने बैलेंस का स्नैपशॉट ले सकता है। यदि उस स्नैपशॉट का उपयोग मतदान के लिए किया जाता है, तो उनके पास उनके निपटान में अनुचित रूप से बड़ी मात्रा में वोट होंगे।
एक फ्लैशलोन बड़ी मात्रा में ईथर या टोकन को एक पते पर उधार देता है, लेकिन अगर उसी लेनदेन में पैसा नहीं चुकाया जाता है तो वापस कर दिया जाता है।
contract SimpleFlashloan { function borrowERC20Tokens() public { uint256 before = token.balanceOf(address(this)); // send tokens to the borrower token.transfer(msg.sender, amount); // hand control back to the borrower to // let them do something IBorrower(msg.sender).onFlashLoan(); // require that the tokens got returned require(token.balanceOf(address(this) >= before); } }
एक हमलावर अपने पक्ष में प्रस्तावों को स्विंग करने और/या कुछ दुर्भावनापूर्ण करने के लिए अचानक बहुत सारे वोट हासिल करने के लिए एक फ्लैश लोन का उपयोग कर सकता है।
यकीनन यह DeFi पर सबसे आम (या कम से कम सबसे हाई-प्रोफाइल) हमला है, जिसमें करोड़ों डॉलर का नुकसान हुआ है। यहां हाई प्रोफाइल लोगों की सूची दी गई है।
ब्लॉकचेन पर किसी संपत्ति की कीमत की गणना अक्सर संपत्तियों के बीच मौजूदा विनिमय दर के रूप में की जाती है। उदाहरण के लिए, यदि कोई अनुबंध वर्तमान में 100 k9कॉइन के लिए 1 USDC का व्यापार कर रहा है, तो आप कह सकते हैं कि k9कॉइन की कीमत 0.01 USDC है। हालांकि, कीमतें आम तौर पर खरीदने और बेचने के दबाव की प्रतिक्रिया में चलती हैं, और त्वरित ऋण बड़े पैमाने पर खरीद और बिक्री का दबाव बना सकते हैं।
संपत्ति की कीमत के बारे में एक और स्मार्ट अनुबंध की पूछताछ करते समय, डेवलपर को बहुत सावधान रहने की जरूरत है क्योंकि वे मान रहे हैं कि जिस स्मार्ट अनुबंध को वे कॉल कर रहे हैं वह फ्लैश ऋण हेरफेर से प्रतिरक्षा है।
यदि कोई पता एक स्मार्ट अनुबंध है, तो उसके बायटेकोड आकार को देखकर आप "चेक" कर सकते हैं। बाहरी स्वामित्व वाले खातों (नियमित वॉलेट) में कोई बायटेकोड नहीं है। इसे करने के कुछ तरीके यहां दिए गए हैं
import "@openzeppelin/contracts/utils/Address.sol" contract CheckIfContract { using Address for address; function addressIsContractV1(address _a) { return _a.code.length == 0; } function addressIsContractV2(address _a) { // use the openzeppelin libraryreturn _a.isContract(); } }
हालाँकि, इसकी कुछ सीमाएँ हैं
सामान्य जांच में यदि कोई पता एक अनुबंध है तो आमतौर पर (लेकिन हमेशा नहीं) एक एंटीपैटर्न होता है। मल्टीसिग्नेचर वॉलेट स्वयं स्मार्ट कॉन्ट्रैक्ट हैं, और ऐसा कुछ भी करना जो मल्टीसिग्नेचर वॉलेट को तोड़ सकता है, कंपोज़िबिलिटी को तोड़ देता है।
इसका अपवाद यह जांच कर रहा है कि ट्रांसफर हुक को कॉल करने से पहले लक्ष्य एक स्मार्ट अनुबंध है या नहीं। इस पर और बाद में।
tx.origin का उपयोग करने का शायद ही कोई अच्छा कारण हो। यदि प्रेषक की पहचान करने के लिए tx.origin का उपयोग किया जाता है, तो एक मैन-इन-द-बीच हमला संभव है। यदि उपयोगकर्ता को एक दुर्भावनापूर्ण स्मार्ट अनुबंध को कॉल करने के लिए बरगलाया जाता है, तो स्मार्ट अनुबंध tx.origin को कहर बरपाने वाले सभी प्राधिकरणों का उपयोग कर सकता है।
इस निम्नलिखित अभ्यास और कोड के ऊपर की टिप्पणियों पर विचार करें।
contract Phish { function phishingFunction() public { // this fails, because this contract does not have approval/allowance token.transferFrom(msg.sender, address(this), token.balanceOf(msg.sender)); // this also fails, because this creates approval for the contract,// not the wallet calling this phishing function token.approve(address(this), type(uint256).max); } }
इसका मतलब यह नहीं है कि आप मनमाना स्मार्ट कॉन्ट्रैक्ट कॉल करने के लिए सुरक्षित हैं। लेकिन अधिकांश प्रोटोकॉल में निर्मित सुरक्षा की एक परत होती है जिसे प्रमाणीकरण के लिए tx.origin का उपयोग करने पर बायपास किया जाएगा।
कभी-कभी, आप ऐसा कोड देख सकते हैं जो इस तरह दिखता है:
require(msg.sender == tx.origin, "no contracts");
जब एक स्मार्ट अनुबंध दूसरे स्मार्ट अनुबंध को कॉल करता है, तो msg.sender स्मार्ट अनुबंध होगा और tx.origin उपयोगकर्ता का बटुआ होगा, इस प्रकार एक विश्वसनीय संकेत देता है कि आने वाली कॉल एक स्मार्ट अनुबंध से है। कन्स्ट्रक्टर से कॉल होने पर भी यह सच है।
ज्यादातर समय, यह डिज़ाइन पैटर्न एक अच्छा विचार नहीं है। EIP 4337 के मल्टीसिग्नेचर वॉलेट और वॉलेट इस कोड वाले फ़ंक्शन के साथ इंटरैक्ट नहीं कर पाएंगे। यह पैटर्न आमतौर पर एनएफटी टकसालों में देखा जा सकता है, जहां यह अपेक्षा करना उचित है कि अधिकांश उपयोगकर्ता पारंपरिक वॉलेट का उपयोग कर रहे हैं। लेकिन जैसे-जैसे खाता अमूर्तता अधिक लोकप्रिय होती जाती है, वैसे-वैसे यह पैटर्न मदद करने से अधिक बाधा उत्पन्न करेगा।
दु: खद हमले का अर्थ है कि हैकर अन्य लोगों के लिए "शोक का कारण" बनने का प्रयास कर रहा है, भले ही उन्हें ऐसा करने से आर्थिक रूप से लाभ न हो।
एक स्मार्ट अनुबंध एक अनंत लूप में प्रवेश करके दुर्भावनापूर्ण रूप से इसे अग्रेषित सभी गैसों का उपयोग कर सकता है। निम्नलिखित उदाहरण पर विचार करें:
contract Mal { fallback() external payable { // infinite loop uses up all the gas while (true) { } } }
यदि कोई अन्य अनुबंध ईथर को पतों की सूची में वितरित करता है जैसे कि:
contract Distribute { funtion distribute(uint256 total) public nonReentrant { for (uint i; i < addresses.length; ) { (bool ok, ) addresses.call{value: total / addresses.length}(""); // ignore ok, if it reverts we move on // traditional gas saving trick for for loops unchecked { ++i; } } } }
तब यह फ़ंक्शन वापस आ जाएगा जब यह मल को ईथर भेजेगा। उपलब्ध गैस के 63/64 से ऊपर के कोड में कॉल, इसलिए संभावना है कि केवल 1/64 गैस के साथ ऑपरेशन को पूरा करने के लिए पर्याप्त गैस नहीं होगी।
एक स्मार्ट कॉन्ट्रैक्ट एक बड़ी मेमोरी ऐरे लौटा सकता है जो बहुत अधिक गैस की खपत करता है
निम्नलिखित उदाहरण पर विचार करें
function largeReturn() public { // result might be extremely long! (book ok, bytes memory result) = otherContract.call(abi.encodeWithSignature("foo()")); require(ok, "call failed"); }
मेमोरी सरणियाँ 724 बाइट्स के बाद द्विघात मात्रा में गैस का उपयोग करती हैं, इसलिए सावधानी से चुना गया रिटर्न डेटा आकार कॉलर को दुखी कर सकता है।
यहां तक कि अगर चर परिणाम का उपयोग नहीं किया जाता है, तब भी इसे मेमोरी में कॉपी किया जाता है। यदि आप रिटर्न साइज को एक निश्चित राशि तक सीमित रखना चाहते हैं, तो आप असेंबली का उपयोग कर सकते हैं
function largeReturn() public { assembly { let ok := call(gas(), destinationAddress, value, dataOffset, dataSize, 0x00, 0x00); // nothing is copied to memory until you // use returndatacopy() } }
हालांकि स्टोरेज को मिटाना एक गैस-कुशल ऑपरेशन है, फिर भी इसकी शुद्ध लागत होती है। यदि कोई सरणी बहुत लंबी हो जाती है, तो उसे हटाना असंभव हो जाता है। यहाँ एक न्यूनतम उदाहरण है
contract VulnerableArray { address[] public stuff; function addSomething(address something) public { stuff.push(something); } // if stuff is too long, this will become undeletable due to // the gas cost function deleteEverything() public onlyOwner { delete stuff; } }
यदि कोई स्मार्ट कॉन्ट्रैक्ट टोकन ट्रांसफर करता है जिसमें ट्रांसफर हुक होते हैं, तो एक हमलावर एक अनुबंध स्थापित कर सकता है जो टोकन को स्वीकार नहीं करता है (या तो इसमें ऑन-रिसीव फ़ंक्शन नहीं होता है या फ़ंक्शन को वापस करने के लिए प्रोग्राम करता है)। यह टोकन को अहस्तांतरणीय बना देगा और पूरे लेनदेन को वापस करने का कारण बनेगा।
SafeTransfer या transfer का उपयोग करने से पहले, इस संभावना पर विचार करें कि रिसीवर लेनदेन को वापस करने के लिए बाध्य कर सकता है।
contract Mal is IERC721Receiver, IERC1155Receiver, IERC777Receiver { // this will intercept any transfer hook fallback() external payable { // infinite loop uses up all the gaswhile (true) { } } // we could also selectively deny transactions function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4) { if (wakeUpChooseViolence()) { revert(); } else { return IERC721Receiver.onERC721Received.selector; } } }
ब्लॉकचैन पर एकल लेनदेन के साथ सुरक्षित रूप से यादृच्छिकता उत्पन्न करना वर्तमान में संभव नहीं है। ब्लॉकचेन को पूरी तरह से निर्धारक होने की आवश्यकता है, अन्यथा वितरित नोड राज्य के बारे में आम सहमति तक नहीं पहुंच पाएंगे। क्योंकि वे पूरी तरह से नियतात्मक हैं, किसी भी "यादृच्छिक" संख्या की भविष्यवाणी की जा सकती है। निम्नलिखित पासा रोलिंग समारोह का फायदा उठाया जा सकता है।
contract UnsafeDice { function randomness() internal returns (uint256) { return keccak256(abi.encode(msg.sender, tx.origin, block.timestamp, tx.gasprice, blockhash(block.number - 1); } // our dice can land on one of {0,1,2,3,4,5}function rollDice() public payable { require(msg.value == 1 ether); if (randomness() % 6) == 5) { msg.sender.call{value: 2 ether}(""); } } } contract ExploitDice { function randomness() internal returns (uint256) { return keccak256(abi.encode(msg.sender, tx.origin, block.timestamp, tx.gasprice, blockhash(block.number - 1); } function betSafely(IUnsafeDice game) public payable { if (randomness % 6) == 5)) { game.betSafely{value: 1 ether}() } // else don't do anything } }
इससे कोई फर्क नहीं पड़ता कि आप कैसे यादृच्छिकता उत्पन्न करते हैं क्योंकि एक हमलावर इसे बिल्कुल दोहरा सकता है। संदेश प्रेषक, टाइमस्टैम्प इत्यादि जैसे "एन्ट्रॉपी" के अधिक स्रोतों में फेंकने से कोई प्रभाव नहीं पड़ेगा क्योंकि स्मार्ट अनुबंध इसे दो माप सकता है।
सुरक्षित यादृच्छिक संख्या प्राप्त करने के लिए चैनलिंक एक लोकप्रिय समाधान है। यह इसे दो चरणों में करता है। सबसे पहले, स्मार्ट अनुबंध ओरेकल को एक यादृच्छिकता अनुरोध भेजता है, फिर कुछ ब्लॉक बाद में, ओरेकल एक यादृच्छिक संख्या के साथ प्रतिक्रिया करता है।
चूंकि एक हमलावर भविष्य की भविष्यवाणी नहीं कर सकता है, वे यादृच्छिक संख्या की भविष्यवाणी नहीं कर सकते।
जब तक स्मार्ट कॉन्ट्रैक्ट ऑरेकल का गलत इस्तेमाल नहीं करता।
चैनलिंक के लिए एक निश्चित समय सीमा के भीतर अपने मूल्य भविष्यवाणी को अद्यतित रखने के लिए कोई SLA (सेवा स्तर समझौता) नहीं है। जब श्रृंखला गंभीर रूप से भीड़भाड़ वाली हो (जैसे कि जब युगा लैब्स अदरसाइड मिंट ने एथेरियम को इतना अभिभूत कर दिया कि कोई लेन-देन नहीं हो रहा है), तो मूल्य अपडेट में देरी हो सकती है।
एक स्मार्ट अनुबंध जो मूल्य ऑरेकल का उपयोग करता है, उसे स्पष्ट रूप से जांचना चाहिए कि डेटा पुराना नहीं है, यानी हाल ही में कुछ सीमा के भीतर अपडेट किया गया है। अन्यथा, यह कीमतों के संबंध में विश्वसनीय निर्णय नहीं ले सकता।
एक अतिरिक्त जटिलता यह है कि यदि मूल्य विचलन सीमा से पहले नहीं बदलता है, तो ओरेकल गैस को बचाने के लिए कीमत को अपडेट नहीं कर सकता है, इसलिए यह उस समय सीमा को प्रभावित कर सकता है जिसे "बासी" माना जाता है।
एक ओरेकल के SLA को समझना महत्वपूर्ण है जिस पर एक स्मार्ट अनुबंध निर्भर करता है।
कोई फर्क नहीं पड़ता कि एक दैवज्ञ कितना सुरक्षित लगता है, भविष्य में एक हमले की खोज की जा सकती है। इसके खिलाफ एकमात्र बचाव कई स्वतंत्र ऑरेकल का उपयोग करना है।
ब्लॉकचेन काफी सुरक्षित हो सकता है, लेकिन पहले चेन पर डेटा डालने के लिए किसी प्रकार के ऑफ-चेन ऑपरेशन की आवश्यकता होती है, जो ब्लॉकचेन द्वारा प्रदान की जाने वाली सभी सुरक्षा गारंटी को छोड़ देता है। भले ही भविष्यवाणियां ईमानदार रहें, उनके डेटा के स्रोत में हेरफेर किया जा सकता है। उदाहरण के लिए, एक ऑरेकल भरोसेमंद रूप से एक केंद्रीकृत एक्सचेंज से कीमतों की रिपोर्ट कर सकता है, लेकिन उन्हें बड़े खरीद और बिक्री के ऑर्डर के साथ जोड़-तोड़ किया जा सकता है। इसी तरह, ऑरेकल जो सेंसर डेटा या कुछ वेब 2 एपीआई पर निर्भर करते हैं, पारंपरिक हैकिंग वैक्टर के अधीन हैं।
एक अच्छा स्मार्ट कॉन्ट्रैक्ट आर्किटेक्चर जहां संभव हो वहां ऑरेकल के उपयोग से पूरी तरह बचता है।
निम्नलिखित अनुबंध पर विचार करें
contract MixedAccounting { uint256 myBalance; function deposit() public payable { myBalance = myBalance + msg.value; } function myBalanceIntrospect() public view returns (uint256) { return address(this).balance; } function myBalanceVariable() public view returns (uint256) { return myBalance; } function notAlwaysTrue() public view returns (bool) { return myBalanceIntrospect() == myBalanceVariable(); } }
उपरोक्त अनुबंध में कोई रिसीव या फॉलबैक फ़ंक्शन नहीं है, इसलिए इसमें सीधे ईथर को स्थानांतरित करना वापस आ जाएगा। हालाँकि, एक अनुबंध बलपूर्वक ईथर को आत्म-विनाश के साथ भेज सकता है।
उस स्थिति में, myBalanceIntrospect() myBalanceVariable() से अधिक होगा। ईथर लेखा पद्धति ठीक है, लेकिन यदि आप दोनों का उपयोग करते हैं, तो अनुबंध में असंगत व्यवहार हो सकता है।
ERC20 टोकन के लिए भी यही बात लागू होती है।
contract MixedAccountingERC20 { IERC20 token; uint256 myTokenBalance; function deposit(uint256 amount) public { token.transferFrom(msg.sender, address(this), amount); myTokenBalance = myTokenBalance + amount; } function myBalanceIntrospect() public view returns (uint256) { return token.balanceOf(address(this)); } function myBalanceVariable() public view returns (uint256) { return myTokenBalance; } function notAlwaysTrue() public view returns (bool) { return myBalanceIntrospect() == myBalanceVariable(); } }
फिर से हम यह नहीं मान सकते हैं कि myBalanceIntrospect() और myBalanceVariable() हमेशा समान मान लौटाएंगे। ERC20 टोकन को सीधे MixedAccountingERC20 में स्थानांतरित करना संभव है, डिपॉजिट फ़ंक्शन को बायपास करना और myTokenBalance वैरिएबल को अपडेट नहीं करना।
आत्मनिरीक्षण के साथ शेष राशि की जाँच करते समय, समानता की जाँच का सख्ती से उपयोग करने से बचना चाहिए क्योंकि शेष राशि को किसी बाहरी व्यक्ति द्वारा इच्छानुसार बदला जा सकता है।
यह सॉलिडिटी की विचित्रता नहीं है, पतों को विशेष विशेषाधिकार देने के लिए क्रिप्टोग्राफी का उपयोग करने के तरीके के बारे में डेवलपर्स के बीच एक आम गलतफहमी है। निम्नलिखित कोड असुरक्षित है
contract InsecureMerkleRoot { bytes32 merkleRoot; function airdrop(bytes[] calldata proof, bytes32 leaf) external { require(MerkleProof.verifyCalldata(proof, merkleRoot, leaf), "not verified"); require(!alreadyClaimed[leaf], "already claimed airdrop"); alreadyClaimed[leaf] = true; mint(msg.sender, AIRDROP_AMOUNT); } }
क्रिप्टोग्राफ़िक प्रूफ़ (मर्कल ट्री, सिग्नेचर, आदि) को msg.sender से बाँधने की आवश्यकता होती है, जिसे एक हमलावर निजी कुंजी प्राप्त किए बिना हेरफेर नहीं कर सकता है।
function limitedMultiply(uint8 a, uint8 b) public pure returns (uint256 product) { product = a * b; }
हालाँकि उत्पाद एक uint256 चर है, गुणन परिणाम 255 से बड़ा नहीं हो सकता है या कोड वापस आ जाएगा।
प्रत्येक चर को अलग-अलग अपकास्टिंग करके इस मुद्दे को कम किया जा सकता है।
function unlimitedMultiply(uint8 a, uint8 b) public pure returns (uint256 product) { product = uint256(a) * uint256(b); }
इस तरह की स्थिति तब हो सकती है जब किसी संरचना में पैक किए गए पूर्णांकों को गुणा किया जाए। संरचना में पैक किए गए छोटे मानों को गुणा करते समय आपको इसका ध्यान रखना चाहिए
struct Packed { uint8 time; uint16 rewardRate } //... Packed p; p.time * p.rewardRate; // this might revert!
सॉलिडिटी यह जाँच नहीं करती है कि पूर्णांक को छोटे पूर्णांक में डालना सुरक्षित है या नहीं। जब तक कुछ व्यावसायिक तर्क यह सुनिश्चित नहीं करते कि डाउनकास्टिंग सुरक्षित है, SafeCast जैसी लाइब्रेरी का उपयोग किया जाना चाहिए।
function test(int256 value) public pure returns (int8) { return int8(value + 1); // overflows and does not revert }
कोड ऐसा लगता है कि यह myArray [1] में डेटा को myArray [0] में कॉपी करता है, लेकिन ऐसा नहीं है। यदि आप फ़ंक्शन में अंतिम पंक्ति पर टिप्पणी करते हैं, तो संकलक कहेगा कि फ़ंक्शन को व्यू फ़ंक्शन में बदल दिया जाना चाहिए। फू को लिखना अंतर्निहित भंडारण को नहीं लिखता है।
contract DoesNotWrite { struct Foo { uint256 bar; } Foo[] public myArray; function moveToSlot0() external { Foo storage foo = myArray[0]; foo = myArray[1]; // myArray[0] is unchanged // we do this to make the function a state // changing operation // and silence the compiler warning myArray[1] = Foo({bar: 100}); } }
इसलिए स्टोरेज पॉइंटर्स को न लिखें।
यदि मैपिंग (या डायनेमिक एरे) किसी स्ट्रक्चर के अंदर है, और स्ट्रक्चर को डिलीट कर दिया जाता है, तो मैपिंग या एरे को डिलीट नहीं किया जाएगा।
किसी सरणी को हटाने के अपवाद के साथ, हटाएं कीवर्ड केवल एक स्टोरेज स्लॉट को हटा सकता है। यदि स्टोरेज स्लॉट में अन्य स्टोरेज स्लॉट्स के संदर्भ हैं, तो उन्हें हटाया नहीं जाएगा।
contract NestedDelete { mapping(uint256 => Foo) buzz; struct Foo { mapping(uint256 => uint256) bar; } Foo foo; function addToFoo(uint256 i) external { buzz[i].bar[5] = 6; } function getFromFoo(uint256 i) external view returns (uint256) { return buzz[i].bar[5]; } function deleteFoo(uint256 i) external { // internal map still holds the data in the // mapping and array delete buzz[i]; } }
याद रखें, सॉलिडिटी में मैप्स कभी भी "खाली" नहीं होते हैं। इसलिए यदि कोई किसी ऐसे आइटम को एक्सेस करता है जिसे हटा दिया गया है, तो लेन-देन वापस नहीं होगा, बल्कि उस डेटाटाइप के लिए शून्य मान लौटाएगा।
यदि आप केवल विश्वसनीय ERC20 टोकन के साथ व्यवहार करते हैं, तो इनमें से अधिकांश मुद्दे लागू नहीं होते हैं। हालांकि, एक मनमाना या आंशिक रूप से अविश्वसनीय ERC20 टोकन के साथ बातचीत करते समय, यहां कुछ बातों का ध्यान रखना चाहिए।
अविश्वसनीय टोकन के साथ काम करते समय, आपको यह नहीं मान लेना चाहिए कि आपकी शेष राशि आवश्यक रूप से राशि से बढ़ जाती है। एक ERC20 टोकन के लिए यह संभव है कि वह इसके हस्तांतरण कार्य को निम्नानुसार कार्यान्वित करे:
contract ERC20 { // internally called by transfer() and transferFrom() // balance and approval checks happen in the caller function _transfer(address from, address to, uint256 amount) internal returns (bool) { fee = amount * 100 / 99; balanceOf[from] -= to; balanceOf[to] += (amount - fee); balanceOf[TREASURY] += fee; emit Transfer(msg.sender, to, (amount - fee)); return true; } }
यह टोकन प्रत्येक लेनदेन पर 1% कर लागू करता है। इसलिए यदि कोई स्मार्ट अनुबंध निम्नानुसार टोकन के साथ इंटरैक्ट करता है, तो हमें या तो अनपेक्षित प्रतिफल मिलेगा या धन की चोरी होगी।
contract Stake { mapping(address => uint256) public balancesInContract; function stake(uint256 amount) public { token.transferFrom(msg.sender, address(this), amount); balancesInContract[msg.sender] += amount; // THIS IS WRONG! } function unstake() public { uint256 toSend = balancesInContract[msg.sender]; delete balancesInContract[msg.sender]; // this could revert because toSend is 1% greater than// the amount in the contract. Otherwise, 1% will be "stolen"// from other depositors. token.transfer(msg.sender, toSend); } }
ओलंपस डीएओ के एसओएचएम टोकन और एम्पलफोर्थ के एएमपीएल टोकन द्वारा रिबेसिंग टोकन को लोकप्रिय बनाया गया था। Coingecko ERC20 टोकन को रिबेस करने की एक सूची रखता है।
जब एक टोकन रीबेस होता है, तो कुल आपूर्ति बदल जाती है और रिबेस की दिशा के आधार पर सभी का संतुलन बढ़ता या घटता है।
रिबेसिंग टोकन के साथ काम करते समय निम्नलिखित कोड के टूटने की संभावना है
contract WillBreak { mapping(address => uint256) public balanceHeld; IERC20 private rebasingToken function deposit(uint256 amount) external { balanceHeld[msg.sender] = amount; rebasingToken.transferFrom(msg.sender, address(this), amount); } function withdraw() external { amount = balanceHeld[msg.sender]; delete balanceHeld[msg.sender]; // ERROR, amount might exceed the amount // actually held by the contract rebasingToken.transfer(msg.sender, amount); } }
कई अनुबंधों का समाधान केवल रीबेसिंग टोकन को अस्वीकार करना है। हालांकि, प्रेषक को खाता शेष राशि स्थानांतरित करने से पहले बैलेंसऑफ (पता (यह)) की जांच करने के लिए ऊपर दिए गए कोड को संशोधित किया जा सकता है। तब यह तब भी काम करेगा, भले ही संतुलन बदल जाए।
ERC20, यदि मानक के अनुसार कार्यान्वित किया जाता है, तो ERC20 टोकन में ट्रांसफर हुक नहीं होते हैं, और इस प्रकार ट्रांसफर और ट्रांसफरफ्रॉम में पुनर्प्रवेश की समस्या नहीं होती है।
ट्रांसफर हुक के साथ टोकन के सार्थक फायदे हैं, यही वजह है कि सभी एनएफटी मानक उन्हें लागू करते हैं, और ईआरसी777 को अंतिम रूप क्यों दिया गया। हालाँकि, इसने पर्याप्त भ्रम पैदा कर दिया है कि Openzeppelin ने ERC777 लाइब्रेरी को अपदस्थ कर दिया ।
यदि आप चाहते हैं कि आपका प्रोटोकॉल उन टोकन के साथ संगत हो जो ERC20 टोकन की तरह व्यवहार करते हैं लेकिन ट्रांसफर हुक हैं, तो यह फ़ंक्शन ट्रांसफर और ट्रांसफरफ्रॉम का इलाज करने का एक साधारण मामला है जैसे वे रिसीवर को फ़ंक्शन कॉल जारी करेंगे।
यह ERC777 पुन: प्रवेश Uniswap के साथ हुआ (यदि आप उत्सुक हैं तो Openzeppelin ने यहां शोषण का दस्तावेजीकरण किया है)।
ERC20 विनिर्देश निर्धारित करता है कि स्थानांतरण सफल होने पर ERC20 टोकन सही होना चाहिए। चूँकि अधिकांश ERC20 कार्यान्वयन तब तक विफल नहीं हो सकते जब तक कि भत्ता अपर्याप्त न हो या स्थानांतरित की गई राशि बहुत अधिक न हो, अधिकांश डेवलपर ERC20 टोकन के वापसी मूल्य की अनदेखी करने के आदी हो गए हैं और एक विफल स्थानांतरण को वापस मान लेते हैं।
स्पष्ट रूप से, यह परिणामी नहीं है यदि आप केवल एक विश्वसनीय ERC20 टोकन के साथ काम कर रहे हैं जिसके व्यवहार को आप जानते हैं। लेकिन मनमाना ERC20 टोकन के साथ व्यवहार करते समय, व्यवहार में इस भिन्नता को ध्यान में रखा जाना चाहिए।
कई अनुबंधों में एक निहित अपेक्षा है कि विफल स्थानान्तरण को हमेशा वापस करना चाहिए, गलत नहीं लौटाना चाहिए क्योंकि अधिकांश ERC20 टोकन में गलत वापसी के लिए कोई तंत्र नहीं होता है, इसलिए इससे बहुत भ्रम पैदा होता है।
इस मामले को और जटिल करते हुए यह है कि कुछ ERC20 टोकन सही वापसी के प्रोटोकॉल का पालन नहीं करते हैं, विशेष रूप से टीथर। कुछ टोकन ट्रांसफर करने में विफल होने पर वापस आ जाते हैं, जिससे कॉल करने वाले को वापस बुलबुला हो जाएगा। इस प्रकार, कुछ पुस्तकालय ERC20 टोकन ट्रांसफर कॉल को रिवर्ट करने के लिए इंटरसेप्ट करते हैं और इसके बजाय एक बूलियन लौटाते हैं।
सोलाडी सेफ ट्रांसफर (काफी अधिक गैस कुशल)
यह एक स्मार्ट अनुबंध भेद्यता नहीं है, लेकिन हम इसे पूर्णता के लिए यहां उल्लेख करते हैं।
विनिर्देश द्वारा शून्य ERC20 टोकन स्थानांतरित करने की अनुमति है। इससे फ्रंटएंड एप्लिकेशन के लिए भ्रम पैदा हो सकता है, और संभावित ट्रिक उपयोगकर्ताओं को यह पता चल सकता है कि उन्होंने हाल ही में किसे टोकन भेजे हैं। इस थ्रेड में मेटामास्क के बारे में और भी बहुत कुछ है।
(वेब 3 भाषा में "बीहड़" का अर्थ है "गलीचा को आपके नीचे से बाहर निकालना।")
ERC20 टोकन में किसी फ़ंक्शन को जोड़ने से किसी को कोई रोक नहीं सकता है जो उन्हें इच्छा पर टोकन बनाने, स्थानांतरित करने और जलाने देता है - या स्व-विनाश या उन्नयन। इसलिए मूल रूप से, ERC20 टोकन कैसे "अविश्वसनीय" हो सकते हैं, इसकी एक सीमा है।
जब यह विचार किया जाता है कि उधार और उधार आधारित डेफी प्रोटोकॉल कैसे टूट सकते हैं, तो यह सोचने में मददगार है कि बग सॉफ्टवेयर स्तर पर फैलते हैं और व्यावसायिक तर्क स्तर को प्रभावित करते हैं। बांड अनुबंध को बनाने और बंद करने के कई चरण होते हैं। यहाँ कुछ अटैक वेक्टर्स पर विचार किया गया है।
यदि संपार्श्विक को प्रोटोकॉल से हटा दिया जाता है, तो ऋणदाता और उधारकर्ता दोनों हार जाते हैं, क्योंकि उधारकर्ता के पास ऋण चुकाने के लिए कोई प्रोत्साहन नहीं होता है, और उधारकर्ता मूलधन खो देता है।
जैसा कि ऊपर देखा जा सकता है, एक DeFi प्रोटोकॉल के "हैक" होने के बहुत अधिक स्तर हैं, जो कि प्रोटोकॉल से निकाले जा रहे धन का एक गुच्छा है (जिस तरह की घटनाएँ आमतौर पर समाचार बनाती हैं)। यह एक ऐसा क्षेत्र है जहां सीटीएफ (ध्वज को पकड़ना) सुरक्षा अभ्यास भ्रामक हो सकता है। हालांकि प्रोटोकॉल फंड चोरी हो जाना सबसे विनाशकारी परिणाम है, यह किसी भी तरह से बचाव के लिए एकमात्र नहीं है।
बाहरी स्मार्ट अनुबंध को कॉल करने के दो तरीके हैं: 1) फ़ंक्शन को इंटरफ़ेस परिभाषा के साथ कॉल करना; 2) .call पद्धति का उपयोग करना। यह नीचे सचित्र है
contract A { uint256 public x; function setx(uint256 _x) external { require(_x > 10, "x must be bigger than 10"); x = _x; } } interface IA { function setx(uint256 _x) external; } contract B { function setXV1(IA a, uint256 _x) external { a.setx(_x); } function setXV2(address a, uint256 _x) external { (bool success, ) = a.call(abi.encodeWithSignature("setx(uint256)", _x)); // success is not checked! } }
अनुबंध बी में, setXV2 चुपचाप विफल हो सकता है यदि _x 10. से कम है। सफलता के मूल्य की जाँच की जानी चाहिए और कोड व्यवहार को उसी के अनुसार शाखा देना चाहिए।
ब्लॉकचेन पर निजी चर अभी भी दिखाई दे रहे हैं, इसलिए संवेदनशील जानकारी को कभी भी वहां संग्रहीत नहीं किया जाना चाहिए। यदि वे पहुंच योग्य नहीं थे, तो सत्यापनकर्ता लेनदेन को कैसे संसाधित कर पाएंगे जो उनके मूल्यों पर निर्भर करता है? निजी चरों को बाहरी सॉलिडिटी अनुबंध से नहीं पढ़ा जा सकता है, लेकिन उन्हें एथेरियम क्लाइंट का उपयोग करके ऑफ-चेन पढ़ा जा सकता है।
किसी वेरिएबल को पढ़ने के लिए, आपको उसके स्टोरेज स्लॉट को जानना होगा। निम्नलिखित उदाहरण में, myPrivateVar का स्टोरेज स्लॉट 0 है।
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract PrivateVarExample { uint256 private myPrivateVar; constructor(uint256 _initialValue) { myPrivateVar = _initialValue; } }
यहाँ तैनात स्मार्ट अनुबंध के निजी चर को पढ़ने के लिए जावास्क्रिप्ट कोड है
const Web3 = require("web3"); const PRIVATE_VAR_EXAMPLE_ADDRESS = "0x123..."; // Replace with your contract address async function readPrivateVar() { const web3 = new Web3("http://localhost:8545"); // Replace with your provider's URL // Read storage slot 0 (where 'myPrivateVar' is stored) const storageSlot = 0; const privateVarValue = await web3.eth.getStorageAt( PRIVATE_VAR_EXAMPLE_ADDRESS, storageSlot ); console.log("Value of private variable 'myPrivateVar':", web3.utils.hexToNumberString(privateVarValue)); } readPrivateVar();
डेलिगेटकॉल का उपयोग कभी भी अविश्वसनीय अनुबंधों के साथ नहीं किया जाना चाहिए क्योंकि यह सभी नियंत्रण डेलिगेट कैली को सौंप देता है। इस उदाहरण में, अविश्वसनीय अनुबंध अनुबंध में सभी ईथर चुरा लेता है।
contract UntrustedDelegateCall { constructor() payable { require(msg.value == 1 ether); } function doDelegateCall(address _delegate, bytes calldata data) public { (bool ok, ) = _delegate.delegatecall(data); require(ok, "delegatecall failed"); } } contract StealEther { function steal() public { // you could also selfdestruct here // if you really wanted to be mean (bool ok,) = tx.origin.call{value: address(this).balance}(""); require(ok); } function attack(address victim) public { UntrustedDelegateCall(victim).doDelegateCall( address(this), abi.encodeWithSignature("steal()")); } }
हम इस विषय के साथ एक खंड में न्याय नहीं कर सकते। Openzeppelin से हार्डहैट प्लगइन का उपयोग करके और यह किन मुद्दों से बचाता है, इसके बारे में पढ़कर अधिकांश अपग्रेड बग्स से बचा जा सकता है। ( https://docs.openzeppelin.com/upgrads-plugins/1.x/ )।
सिर्फ इसलिए कि एक अनुबंध का एक मालिक या एक व्यवस्थापक है, इसका मतलब यह नहीं है कि उनकी शक्ति असीमित होनी चाहिए। एक एनएफटी पर विचार करें। यह केवल मालिक के लिए एनएफटी बिक्री से कमाई को वापस लेने के लिए उचित है, लेकिन अगर मालिक की निजी चाबियों से समझौता किया जाता है तो अनुबंध (ब्लॉक ट्रांसफर) को रोकने में सक्षम होने से कहर बरपा सकता है। आम तौर पर, अनावश्यक जोखिम को कम करने के लिए प्रशासक के विशेषाधिकार यथासंभव कम से कम होने चाहिए।
अनुबंध स्वामित्व की बात हो रही है ...
यह तकनीकी रूप से एक भेद्यता नहीं है, लेकिन यदि स्वामित्व गैर-मौजूद पते पर स्थानांतरित हो जाता है, तो OpenZeppelin स्वामित्व अनुबंध स्वामित्व के नुकसान का कारण बन सकता है। Ownerable2step के लिए प्राप्तकर्ता को स्वामित्व की पुष्टि करने की आवश्यकता होती है। यह गलत टाइप किए गए पते पर गलती से स्वामित्व भेजने के विरुद्ध बीमा करता है।
सॉलिडिटी में फ्लोट्स नहीं होते हैं, इसलिए राउंडिंग एरर अपरिहार्य हैं। डिज़ाइनर को इस बात के प्रति जागरूक होना चाहिए कि राउंड अप करना सही है या राउंड डाउन करना और राउंडिंग किसके पक्ष में होनी चाहिए।
विभाजन हमेशा अंतिम किया जाना चाहिए। निम्न कोड गलत तरीके से उन स्थिर मुद्राओं के बीच परिवर्तित हो जाता है जिनमें दशमलव की भिन्न संख्या होती है। दाई (जिसमें 18 दशमलव हैं) के लिए विनिमय करते समय निम्नलिखित विनिमय तंत्र एक उपयोगकर्ता को USDC (जिसमें 6 दशमलव हैं) की एक छोटी राशि मुफ्त में लेने की अनुमति देता है। परिवर्तनीय daiToTake एक गैर-शून्य usdcAmount के बदले में उपयोगकर्ता से कुछ भी नहीं लेते हुए, शून्य तक पहुंच जाएगा।
contract Exchange { uint256 private constant CONVERSION = 1e12; function swapDAIForUSDC(uint256 usdcAmount) external pure returns (uint256 a) { uint256 daiToTake = usdcAmount / CONVERSION; conductSwap(daiToTake, usdcAmount); } }
एथेरियम (और इसी तरह की श्रृंखला) के संदर्भ में आगे बढ़ने का अर्थ है एक लंबित लेन-देन का अवलोकन करना और उच्च गैस मूल्य का भुगतान करके इससे पहले एक अन्य लेनदेन को निष्पादित करना। यही है, हमलावर ने लेन-देन के "सामने भाग" लिया है। यदि लेन-देन एक लाभदायक व्यापार है, तो उच्च गैस मूल्य का भुगतान करने के अलावा लेनदेन को बिल्कुल कॉपी करना समझ में आता है। इस घटना को कभी-कभी एमईवी के रूप में संदर्भित किया जाता है, जिसका अर्थ है खनिक निकालने योग्य मूल्य, लेकिन कभी-कभी अन्य संदर्भों में अधिकतम निकालने योग्य मूल्य। ब्लॉक उत्पादकों के पास लेन-देन को फिर से व्यवस्थित करने और अपने स्वयं के सम्मिलित करने की असीमित शक्ति होती है, और ऐतिहासिक रूप से, एथेरियम के दांव के प्रमाण में जाने से पहले ब्लॉक निर्माता खनिक थे, इसलिए नाम।
स्मार्ट अनुबंध से ईथर को वापस लेने को "लाभदायक व्यापार" माना जा सकता है। आप एक शून्य-लागत लेनदेन (गैस से अलग) निष्पादित करते हैं और आपके द्वारा शुरू की गई तुलना में अधिक क्रिप्टोकुरेंसी के साथ समाप्त हो जाते हैं।
contract UnprotectedWithdraw { constructor() payable { require(msg.value == 1 ether, "must create with 1 eth"); } function unsafeWithdraw() external { (bool ok, ) = msg.sender.call{value: address(this).value}(""); require(ok, "transfer failed"). } }
यदि आप इस अनुबंध को तैनात करते हैं और वापस लेने का प्रयास करते हैं, तो एक अग्रणी बॉट आपके कॉल को मेमपूल में "असुरक्षित निकासी" के लिए नोटिस करेगा और पहले ईथर प्राप्त करने के लिए इसे कॉपी करेगा।
हमने अपने ERC4626 ट्यूटोरियल में ERC-4626 मुद्रास्फीति हमले के बारे में गहराई से लिखा है। लेकिन इसका सार यह है कि एक ERC4626 अनुबंध "संपत्ति" के प्रतिशत के आधार पर "शेयर" टोकन वितरित करता है जो एक व्यापारी योगदान देता है।
मोटे तौर पर यह निम्नानुसार काम करता है:
function getShares(...) external { // code shares_received = assets_contributed / total_assets; // more code }
बेशक, कोई भी संपत्ति का योगदान नहीं करेगा और कोई शेयर वापस नहीं मिलेगा, लेकिन वे भविष्यवाणी नहीं कर सकते हैं कि अगर कोई शेयर प्राप्त करने के लिए व्यापार को आगे बढ़ा सकता है।
उदाहरण के लिए, वे 200 संपत्तियों का योगदान करते हैं जब पूल में 20 होते हैं, तो वे 100 शेयर प्राप्त करने की उम्मीद करते हैं। लेकिन अगर कोई 200 संपत्ति जमा करने के लिए लेन-देन को आगे बढ़ाता है, तो सूत्र 200/220 होगा, जो शून्य से कम हो जाता है, जिससे पीड़ित को संपत्ति खोनी पड़ती है और शून्य शेयर वापस मिलते हैं।
सार में इसका वर्णन करने के बजाय इसे वास्तविक उदाहरण के साथ चित्रित करना सबसे अच्छा है
अब ईव के पास 100 या 50 के बजाय 150 टोकन हैं। इसका समाधान अविश्वसनीय अनुमोदनों से निपटने के दौरान इसे बढ़ाने या घटाने से पहले अनुमोदन को शून्य पर सेट करना है।
किसी संपत्ति की कीमत खरीदने और बेचने के दबाव के जवाब में चलती है। यदि एक बड़ा ऑर्डर मेमपूल में बैठा है, तो व्यापारियों को ऑर्डर कॉपी करने के लिए एक प्रोत्साहन मिलता है, लेकिन उच्च गैस की कीमत के साथ। इस तरह, वे संपत्ति खरीदते हैं, बड़े ऑर्डर को कीमत बढ़ने देते हैं, फिर वे तुरंत बेच देते हैं। बेचने के आदेश को कभी-कभी "बैकरनिंग" कहा जाता है। बेचने के आदेश को कम गैस की कीमत के साथ बिक्री आदेश देकर किया जा सकता है ताकि क्रम इस तरह दिखाई दे
इस हमले के खिलाफ प्राथमिक बचाव "स्लिपेज" पैरामीटर प्रदान करना है। यदि "फ्रंट्रन बाय" खुद ही कीमत को एक निश्चित थ्रेशल्ड से ऊपर धकेल देता है, तो "लार्ज बाय" ऑर्डर वापस आ जाएगा, जिससे फ्रंटरनर व्यापार में विफल हो जाएगा।
इसे सैंडविच कहा जाता है, क्योंकि बड़ी खरीद को फ्रंटरन खरीद और बैकरन सेल द्वारा सैंडविच किया जाता है। यह हमला ठीक विपरीत दिशा में बड़े विक्रय आदेशों के साथ भी काम करता है।
फ्रंटरनिंग एक विशाल विषय है। फ्लैशबॉट्स ने इस विषय पर बड़े पैमाने पर शोध किया है और इसके नकारात्मक बाह्यताओं को कम करने में मदद के लिए कई उपकरण और शोध लेख प्रकाशित किए हैं।
उचित ब्लॉकचेन आर्किटेक्चर के साथ फ्रंटरनिंग को "डिज़ाइन किया गया" हो सकता है या नहीं, यह बहस का विषय है जिसे निर्णायक रूप से सुलझाया नहीं गया है। निम्नलिखित दो लेख इस विषय पर स्थायी कालजयी हैं:
स्मार्ट अनुबंधों के संदर्भ में डिजिटल हस्ताक्षर के दो उपयोग हैं:
उपयोगकर्ता को NFT बनाने का विशेषाधिकार देने के लिए सुरक्षित रूप से डिजिटल हस्ताक्षर का उपयोग करने का एक उदाहरण यहां दिया गया है:
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract NFT is ERC721("name", "symbol") { function mint(bytes calldata signature) external { address recovered = keccak256(abi.encode(msg.sender)).toEthSignedMessageHash().recover(signature); require(recovered == authorizer, "signature does not match"); } }
एक उत्कृष्ट उदाहरण ERC20 में स्वीकृत कार्यक्षमता है। हमारे खाते से एक निश्चित मात्रा में टोकन निकालने के लिए एक पते को स्वीकृत करने के लिए, हमें एक वास्तविक एथेरियम लेनदेन करना होगा, जिसमें गैस खर्च होती है।
प्राप्तकर्ता ऑफ-चेन को डिजिटल हस्ताक्षर पास करना कभी-कभी अधिक कुशल होता है, फिर प्राप्तकर्ता स्मार्ट अनुबंध पर हस्ताक्षर की आपूर्ति करता है ताकि यह साबित हो सके कि वे लेन-देन करने के लिए अधिकृत थे।
ERC20Permit एक डिजिटल हस्ताक्षर के साथ अनुमोदन सक्षम करता है। समारोह निम्नानुसार वर्णित है
function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public
वास्तविक स्वीकृत लेन-देन भेजने के बजाय, स्वामी व्ययकर्ता के लिए अनुमोदन पर "हस्ताक्षर" कर सकता है (एक समय सीमा के साथ)। स्वीकृत व्ययकर्ता तब प्रदान किए गए मापदंडों के साथ परमिट फ़ंक्शन को कॉल कर सकता है।
आप चर, v, r, और s बार-बार देखेंगे। वे क्रमशः डेटाटाइप्स uint8, बाइट्स32, और बाइट्स32 के साथ सॉलिडिटी में प्रदर्शित होते हैं। कभी-कभी, हस्ताक्षर को 65 बाइट सरणी के रूप में दर्शाया जाता है, जो इन सभी मानों को abi.encodePacked(r, s, v);
एक हस्ताक्षर के अन्य दो आवश्यक घटक संदेश हैश (32 बाइट्स) और हस्ताक्षर करने का पता हैं। क्रम इस प्रकार है
एक सार्वजनिक पता (ethAddress) उत्पन्न करने के लिए एक निजी कुंजी (privKey) का उपयोग किया जाता है
एक स्मार्ट कॉन्ट्रैक्ट ethAddress को पहले से स्टोर कर लेता है
एक ऑफचैन उपयोगकर्ता एक संदेश हैश करता है और हैश पर हस्ताक्षर करता है। यह जोड़ी msgHash और हस्ताक्षर (आर, एस, वी) का उत्पादन करता है
स्मार्ट कॉन्ट्रैक्ट एक संदेश प्राप्त करता है, इसे msgHash उत्पन्न करने के लिए हैश करता है, फिर इसे (r, s, v) के साथ जोड़ता है यह देखने के लिए कि कौन सा पता निकलता है।
यदि पता ethAddress से मेल खाता है, तो हस्ताक्षर मान्य है (कुछ मान्यताओं के तहत जो हम जल्द ही देखेंगे!)
स्मार्ट कॉन्ट्रैक्ट चरण 4 में प्रीकंपिल्ड कॉन्ट्रैक्ट ईक्रेकवर का उपयोग करते हैं जिसे हम संयोजन कहते हैं और पता वापस प्राप्त करते हैं।
इस प्रक्रिया में बहुत सारे कदम हैं जहां चीजें किनारे जा सकती हैं।
यह भेद्यता का कारण बन सकता है यदि एक गैर-प्रारंभिक चर की तुलना ईक्रेकवर के आउटपुट से की जाती है।
यह कोड असुरक्षित है
contract InsecureContract { address signer; // defaults to address(0) // who lets us give the beneficiary the airdrop without them// spending gas function airdrop(address who, uint256 amount, uint8 v, bytes32 r, bytes32 s) external { // ecrecover returns address(0) if the signature is invalid require(signer == ecrecover(keccak256(abi.encode(who, amount)), v, r, s), "invalid signature"); mint(msg.sender, AIRDROP_AMOUNT); } }
सिग्नेचर रिप्ले तब होता है जब कोई कॉन्ट्रैक्ट ट्रैक नहीं करता है कि पहले सिग्नेचर का इस्तेमाल किया गया है या नहीं। निम्नलिखित कोड में, हम पिछले मुद्दे को ठीक करते हैं, लेकिन यह अभी भी सुरक्षित नहीं है।
contract InsecureContract { address signer; function airdrop(address who, uint256 amount, uint8 v, bytes32 r, bytes32 s) external { address recovered == ecrecover(keccak256(abi.encode(who, amount)), v, r, s); require(recovered != address(0), "invalid signature"); require(recovered == signer, "recovered signature not equal signer"); mint(msg.sender, amount); } }
लोग जितनी बार चाहें एयरड्रॉप का दावा कर सकते हैं!
हम निम्नलिखित पंक्तियाँ जोड़ सकते हैं
bytes memory signature = abi.encodePacked(v, r, s); require(!used[signature], "signature already used"); // mapping(bytes => bool); used[signature] = true;
काश, कोड अभी भी सुरक्षित नहीं होता!
एक वैध हस्ताक्षर दिए जाने पर, हमलावर एक अलग हस्ताक्षर प्राप्त करने के लिए कुछ त्वरित अंकगणित कर सकता है। हमलावर तब इस संशोधित हस्ताक्षर को "पुनः चला" सकता है। लेकिन पहले, आइए कुछ कोड प्रदान करें जो दर्शाता है कि हम एक वैध हस्ताक्षर के साथ शुरू कर सकते हैं, इसे संशोधित कर सकते हैं और दिखा सकते हैं कि नया हस्ताक्षर अभी भी पास है।
contract Malleable { // v = 28 // r = 0xf8479d94c011613baeffe9239e4ff65e2adbac744c34217ca7d51378e72c5204 // s = 0x57af17590a914b759c45aaeabaf513d5ef72d7da1bdd19d9f2e1bc371ece5b86 // m = 0x0000000000000000000000000000000000000000000000000000000000000003 function foo(bytes calldata msg, uint8 v, bytes32 r, bytes32 s) public pure returns (address, address){ bytes32 h = keccak256(msg); address a = ecrecover(h, v, r, s); // The following is math magic to invert the // signature and create a valid one // flip s bytes32 s2 = bytes32(uint256(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141) - uint256(s)); // invert v uint8 v2; require(v == 27 || v == 28, "invalid v"); v2 = v == 27 ? 28 : 27; address b = ecrecover(h, v2, r, s2); assert(a == b); // different signatures, same address!; return (a, b); } }
इस प्रकार, हमारा चल रहा उदाहरण अभी भी असुरक्षित है। एक बार जब कोई वैध हस्ताक्षर प्रस्तुत करता है, तो इसका दर्पण छवि हस्ताक्षर बनाया जा सकता है और प्रयुक्त हस्ताक्षर जांच को बाईपास कर सकता है।
contract InsecureContract { address signer; function airdrop(address who, uint256 amount, uint8 v, bytes32 r, bytes32 s) external { address recovered == ecrecover(keccak256(abi.encode(who, amount)), v, r, s); require(recovered != address(0), "invalid signature"); require(recovered == signer, "recovered signature not equal signer"); bytes memory signature = abi.encodePacked(v, r, s); require(!used[signature], "signature already used"); // this can be bypassed used[signature] = true; mint(msg.sender, amount); } }
आप शायद इस बिंदु पर कुछ सुरक्षित हस्ताक्षर कोड चाहते हैं, है ना? हम आपको सॉलिडिटी में सिग्नेचर बनाने और फाउंड्री में उनका परीक्षण करने के बारे में हमारे ट्यूटोरियल का संदर्भ देते हैं।
लेकिन यहाँ चेकलिस्ट है।
यदि चेन पर हैशिंग नहीं की जाती है तो उपरोक्त हमले को और सामान्यीकृत किया जा सकता है। उपरोक्त उदाहरणों में, हैशिंग स्मार्ट अनुबंध में की गई थी, इसलिए उपरोक्त उदाहरण निम्नलिखित शोषण के प्रति संवेदनशील नहीं हैं।
आइए हस्ताक्षर पुनर्प्राप्त करने के लिए कोड देखें
// this code is vulnerable! function recoverSigner(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public returns (address signer) { require(signer == ecrecover(hash, v, r, s), "signer does not match"); // more actions }
उपयोगकर्ता हैश और हस्ताक्षर दोनों की आपूर्ति करता है। यदि हमलावर ने पहले ही हस्ताक्षरकर्ता से एक वैध हस्ताक्षर देख लिया है, तो वे दूसरे संदेश के हैश और हस्ताक्षर का पुन: उपयोग कर सकते हैं।
यही कारण है कि स्मार्ट कॉन्ट्रैक्ट में संदेश को हैश करना बहुत महत्वपूर्ण है, न कि ऑफ-चेन।
इस कारनामे को कार्रवाई में देखने के लिए, हमारे द्वारा ट्विटर पर पोस्ट किया गया CTF देखें।
भाग 1: https://twitter.com/RareSkills_io/status/1650869999266037760
भाग 2: https://twitter.com/RareSkills_io/status/1650897671543197701
https://twitter.com/RareSkills_io/status/1651527648676573185 https://twitter.com/RareSkills_io/status/1651224817465540611
उपयोगकर्ताओं की पहचान करने के लिए हस्ताक्षरों का उपयोग नहीं किया जाना चाहिए। निंदनीयता के कारण, उन्हें अद्वितीय नहीं माना जा सकता है। Msg.sender के पास बहुत मजबूत अद्वितीयता की गारंटी है।
हमारे द्वारा ट्विटर पर होस्ट किया गया सुरक्षा अभ्यास यहां देखें। कोडबेस का ऑडिट करते समय, सॉलिडिटी पेज पर रिलीज़ घोषणाओं के विरुद्ध सॉलिडिटी संस्करण की जाँच करें ताकि यह देखा जा सके कि कोई बग मौजूद हो सकता है या नहीं।
स्मार्ट कॉन्ट्रैक्ट्स को प्रॉक्सी पैटर्न (या शायद ही कभी, मेटामॉर्फिक पैटर्न) के साथ अपग्रेड किया जा सकता है। स्मार्ट अनुबंधों को अपरिवर्तित बने रहने के लिए मनमाने ढंग से स्मार्ट अनुबंधों की कार्यक्षमता पर भरोसा नहीं करना चाहिए।
सॉलिडिटी फ़ंक्शंस ट्रांसफर और सेंड का उपयोग नहीं किया जाना चाहिए। वे जानबूझकर लेन-देन के साथ अग्रेषित गैस की मात्रा को 2,300 तक सीमित कर देते हैं, जिससे अधिकांश परिचालन गैस से बाहर हो जाएंगे।
आमतौर पर इस्तेमाल किया जाने वाला ग्नोसिस सेफ मल्टी-सिग्नेचर वॉलेट फ़ॉलबैक फ़ंक्शन में कॉल को दूसरे पते पर अग्रेषित करने का समर्थन करता है। यदि कोई मल्टीसिग वॉलेट में ईथर भेजने के लिए ट्रांसफर या सेंड का उपयोग करता है, तो फ़ॉलबैक फ़ंक्शन गैस से बाहर हो सकता है और स्थानांतरण विफल हो जाएगा। ग्नोसिस सेफ फॉलबैक फंक्शन का स्क्रीनशॉट नीचे दिया गया है। पाठक स्पष्ट रूप से देख सकते हैं कि 2300 गैस का उपयोग करने के लिए पर्याप्त से अधिक ऑपरेशन हैं।
यदि आपको एक अनुबंध के साथ बातचीत करने की आवश्यकता है जो हस्तांतरण और भेजने का उपयोग करता है, तो एथेरियम एक्सेस सूची लेनदेन पर हमारा लेख देखें जो आपको भंडारण और अनुबंध पहुंच संचालन की गैस लागत को कम करने की अनुमति देता है।
सॉलिडिटी 0.8.0 बिल्ट इन ओवरफ्लो और अंडरफ्लो प्रोटेक्शन है। इसलिए जब तक कोई अनियंत्रित ब्लॉक मौजूद नहीं है, या यूल में निम्न स्तर के कोड का उपयोग नहीं किया जाता है, तब तक अतिप्रवाह का कोई खतरा नहीं है। इसलिए, सेफमैथ पुस्तकालयों का उपयोग नहीं किया जाना चाहिए क्योंकि वे अतिरिक्त जांचों पर गैस बर्बाद करते हैं।
कुछ साहित्य दस्तावेज़ जो ब्लॉक.टाइमस्टैम्प एक भेद्यता वेक्टर है क्योंकि खनिक इसमें हेरफेर कर सकते हैं। यह आमतौर पर यादृच्छिकता के स्रोत के रूप में टाइमस्टैम्प का उपयोग करने के लिए लागू होता है, जिसे वैसे भी पहले दस्तावेज के रूप में नहीं किया जाना चाहिए। पोस्ट-मर्ज एथेरियम टाइमस्टैम्प को ठीक 12 सेकंड (या 12 सेकंड के गुणकों) के अंतराल में अपडेट करता है। हालाँकि, दूसरे स्तर के ग्रैन्युलैरिटी में समय मापना एक प्रतिमान है। एक मिनट के पैमाने पर, यदि एक सत्यापनकर्ता अपने ब्लॉक स्लॉट को याद करता है और ब्लॉक उत्पादन में 24 सेकंड का अंतर होता है, तो त्रुटि के लिए काफी अवसर होता है।
कोने के मामलों को आसानी से परिभाषित नहीं किया जा सकता है, लेकिन एक बार जब आप उन्हें पर्याप्त रूप से देख लेते हैं, तो आप उनके लिए एक अंतर्ज्ञान विकसित करना शुरू कर देते हैं। एक कोने का मामला कुछ ऐसा हो सकता है जैसे कोई इनाम का दावा करने की कोशिश कर रहा हो, लेकिन कुछ भी दांव पर न लगा हो। यह मान्य है, हमें उन्हें केवल शून्य पुरस्कार देना चाहिए। इसी तरह, हम आम तौर पर पुरस्कारों को समान रूप से विभाजित करना चाहते हैं, लेकिन क्या होगा यदि केवल एक प्राप्तकर्ता हो और तकनीकी रूप से कोई विभाजन न हो?
यह उदाहरण अक्षय श्रीवास्तव के ट्विटर थ्रेड से लिया गया और संशोधित किया गया।
उस मामले पर विचार करें जहां कोई विशेषाधिकार प्राप्त कार्रवाई कर सकता है यदि विशेषाधिकार प्राप्त पतों का एक सेट इसके लिए हस्ताक्षर प्रदान करता है।
contract VulnerableMultisigAuthorization { struct Authorization { bytes signature; address authorizer; bytes32 hashOfAction; // more fields } // more codef unction takeAction(Authorization[] calldata auths, bytes calldata action) public { // logic for avoiding replay attacks for (uint256 i; i < auths.length; ++i) { require(validateSignature(auths[i].signature, auths[i].authorizer), "invalid signature"); require(authorizers[auths[i].authorizer], "address is not an authorizer"); } doTheAction(action) } }
यदि कोई भी हस्ताक्षर मान्य नहीं है, या हस्ताक्षर वैध पते से मेल नहीं खाते हैं, तो वापस कर दिया जाएगा। लेकिन क्या होगा अगर सरणी खाली है? उस स्थिति में, यह किसी भी हस्ताक्षर की आवश्यकता के बिना कार्रवाई करने के लिए नीचे कूद जाएगा।
contract ProportionalRewards { mapping(address => uint256) originalId; address[] stakers; function stake(uint256 id) public { nft.transferFrom(msg.sender, address(this), id); stakers.append(msg.sender); } function unstake(uint256 id) public { require(originalId[id] == msg.sender, "not the owner"); removeFromArray(msg.sender, stakers); sendRewards(msg.sender, totalRewardsSinceLastclaim() / stakers.length()); nft.transferFrom(address(this), msg.sender, id); } }
यद्यपि उपरोक्त कोड सभी फ़ंक्शन कार्यान्वयन नहीं दिखाता है, भले ही फ़ंक्शन उनके नाम के अनुसार व्यवहार करते हैं, फिर भी एक बग है। क्या आप इसका पता लगा सकते हैं? नीचे स्क्रॉल करने से पहले आपको उत्तर न देखने के लिए कुछ जगह देने के लिए यहां एक तस्वीर दी गई है।
रिमूवफ्रॉमएरे और सेंड रिवार्ड्स फंक्शन गलत क्रम में हैं। यदि स्टेकर्स सरणी में केवल एक उपयोगकर्ता है, तो शून्य त्रुटि से विभाजित होगा, और उपयोगकर्ता अपने एनएफटी को वापस लेने में सक्षम नहीं होगा। इसके अलावा, पुरस्कारों को लेखक के इरादे से विभाजित नहीं किया जा सकता है। यदि मूल चार हितधारक थे, और एक व्यक्ति वापस ले लेता है, तो उसे एक तिहाई पुरस्कार मिलेगा क्योंकि वापसी के समय सरणी की लंबाई 3 है।
आइए एक वास्तविक उदाहरण का उपयोग करें कि कुछ अनुमानों के कारण $100 मिलियन डॉलर से अधिक की क्षति हुई है। चिंता न करें यदि आप कंपाउंड प्रोटोकॉल को पूरी तरह से नहीं समझते हैं, तो हम केवल संबंधित भागों पर ध्यान केंद्रित करेंगे। (साथ ही कंपाउंड प्रोटोकॉल डेफी के इतिहास में सबसे महत्वपूर्ण और परिणामी प्रोटोकॉल में से एक है, हम इसे अपने डेफी बूटकैंप में पढ़ाते हैं, इसलिए यदि यह प्रोटोकॉल का आपका पहला प्रभाव है, तो गुमराह न हों)।
वैसे भी, कंपाउंड का बिंदु उपयोगकर्ताओं को अपने निष्क्रिय क्रिप्टोकुरेंसी को अन्य व्यापारियों को उधार देने के लिए पुरस्कृत करना है, जिनके पास इसका उपयोग हो सकता है। उधारदाताओं को ब्याज और COMP टोकन दोनों में भुगतान किया जाता है (उधारकर्ता COMP टोकन इनाम का दावा कर सकते हैं, लेकिन हम अभी उस पर ध्यान केंद्रित नहीं करेंगे)।
कंपाउंड नियंत्रक एक प्रॉक्सी अनुबंध है जो कंपाउंड गवर्नेंस द्वारा सेट किए जा सकने वाले कार्यान्वयन के लिए कॉल करता है।
30 सितंबर, 2021 को शासन के प्रस्ताव 62 में, कार्यान्वयन अनुबंध को एक कार्यान्वयन अनुबंध पर सेट किया गया था जिसमें भेद्यता थी। उसी दिन यह लाइव हो गया, ट्विटर पर यह देखा गया कि शून्य टोकन के बावजूद कुछ लेन-देन COMP पुरस्कारों का दावा कर रहे थे।
असुरक्षित कार्य वितरितSupplierComp()
यहाँ मूल कोड है
बग, विडंबना यह है कि TODO टिप्पणी में है। "यदि उपयोगकर्ता आपूर्तिकर्ता बाजार में नहीं है तो आपूर्तिकर्ता COMP वितरित न करें।" लेकिन उसके लिए कोड में कोई चेक नहीं है। जब तक उपयोगकर्ता अपने वॉलेट में स्टेकिंग टोकन रखता है (CToken(cToken).balanceOf(आपूर्तिकर्ता);), तब
प्रस्ताव 64 ने 9 अक्टूबर, 2021 को बग को ठीक कर दिया।
हालांकि यह एक इनपुट सत्यापन बग होने का तर्क दिया जा सकता है, उपयोगकर्ताओं ने कुछ भी दुर्भावनापूर्ण सबमिट नहीं किया। अगर कोई कुछ भी दांव पर नहीं लगाने के लिए इनाम का दावा करने की कोशिश करता है, तो सही गणना शून्य होनी चाहिए। यकीनन, यह एक व्यावसायिक तर्क या कोने के मामले की त्रुटि है।
वास्तविक दुनिया में होने वाले डेफी हैक अक्सर उपरोक्त अच्छी श्रेणियों में नहीं आते हैं।
पैरिटी वॉलेट को सीधे इस्तेमाल करने का इरादा नहीं था। यह एक संदर्भ कार्यान्वयन था जिसे स्मार्ट कॉन्ट्रैक्ट क्लोन इंगित करेगा। वांछित होने पर क्लोनों को स्वयं को नष्ट करने के लिए कार्यान्वयन की अनुमति दी गई, लेकिन इसके लिए सभी वॉलेट मालिकों को इस पर हस्ताक्षर करने की आवश्यकता थी।
// throw unless the contract is not yet initialized.modifier only_uninitialized { if (m_numOwners > 0) throw; _; } function initWallet(address[] _owners, uint _required, uint _daylimit) only_uninitialized { initDaylimit(_daylimit); initMultiowned(_owners, _required); }
बटुए के मालिक घोषित किए जाते हैं
// kills the contract sending everything to `_to`.function kill(address _to) onlymanyowners(sha3(msg.data)) external { suicide(_to); }
कुछ साहित्य इसे "असुरक्षित आत्मविनाश" के रूप में वर्णित करते हैं, अर्थात अभिगम नियंत्रण विफलता, लेकिन यह बिल्कुल सटीक नहीं है। समस्या यह थी कि initWallet फ़ंक्शन को कार्यान्वयन अनुबंध पर नहीं बुलाया गया था और इसने किसी को initWallet फ़ंक्शन को स्वयं कॉल करने और स्वयं को स्वामी बनाने की अनुमति दी थी। इससे उन्हें किल फंक्शन को बुलाने का अधिकार मिल गया। मूल कारण यह था कि कार्यान्वयन प्रारंभ नहीं किया गया था। इसलिए, बग को दोषपूर्ण सॉलिडिटी कोड के कारण नहीं, बल्कि दोषपूर्ण परिनियोजन प्रक्रिया के कारण पेश किया गया था।
इस हैक में किसी सॉलिडिटी कोड का फायदा नहीं उठाया गया। इसके बजाय, हमलावर क्लाउडफ्लेयर एपीआई कुंजी प्राप्त करते हैं और वेबसाइट के फ्रंटएंड में एक स्क्रिप्ट इंजेक्ट करते हैं जो उपयोगकर्ता लेनदेन को हमलावर के पते पर प्रत्यक्ष निकासी के लिए बदल देता है। इस लेख में और पढ़ें।
बहुत सारे अग्रणी शून्य वाले पतों की खोज के लिए प्रेरणा यह है कि वे उपयोग करने के लिए अधिक गैस कुशल हैं। एक एथेरियम लेनदेन को लेनदेन डेटा में शून्य बाइट के लिए 4 गैस और गैर-शून्य बाइट के लिए 16 गैस चार्ज किया जाता है।
जैसे, विंटरम्यूट को हैक कर लिया गया था क्योंकि इसमें अपशब्दों का इस्तेमाल किया गया था ( राइटअप )। यहाँ 1 इंच का लेखन है कि किस तरह अपवित्रता पता जनरेटर से समझौता किया गया था।
इस लेख में ट्रस्ट वॉलेट में एक समान भेद्यता का दस्तावेजीकरण किया गया था ( https://blog.ledger.com/Funds-of-every-wallet-created-with-the-Trust-Wallet-browser-extension-could-have-been- चोरी/ )
ध्यान दें कि यह create2 में नमक को बदलकर खोजे गए प्रमुख शून्य वाले स्मार्ट अनुबंधों पर लागू नहीं होता है, क्योंकि स्मार्ट अनुबंधों में निजी कुंजी नहीं होती है।
अंडाकार वक्र हस्ताक्षर पर "आर" और "एस" बिंदु निम्नानुसार उत्पन्न होता है
r = k * G (mod N) s = k^-1 * (h + r * privateKey) (mod N)
जी, आर, एस, एच, एन सभी सार्वजनिक रूप से जाने जाते हैं। यदि "के" सार्वजनिक हो जाता है, तो "निजीकी" एकमात्र अज्ञात चर है, और इसके लिए हल किया जा सकता है। इस वजह से वॉलेट को पूरी तरह से बेतरतीब ढंग से k उत्पन्न करने की आवश्यकता होती है और इसे कभी भी पुन: उपयोग नहीं करना चाहिए। यदि यादृच्छिकता पूरी तरह से यादृच्छिक नहीं है, तो k का अनुमान लगाया जा सकता है।
जावा लाइब्रेरी में असुरक्षित यादृच्छिकता पीढ़ी ने 2013 में बहुत सारे एंड्रॉइड बिटकॉइन वॉलेट को कमजोर कर दिया। (बिटकॉइन एथेरियम के समान हस्ताक्षर एल्गोरिदम का उपयोग करता है।)
( https://arstechnica.com/ सूचना-प्रौद्योगिकी/2013/08/all-android-created-bitcoin-wallets-vulnerable-to-theft/)।
इस सूची में एंटी-पैटर्न को जल्दी से पहचानने के लिए खुद को प्रशिक्षित करने से आप एक अधिक प्रभावी स्मार्ट कॉन्ट्रैक्ट प्रोग्रामर बन जाएंगे, लेकिन परिणाम के अधिकांश स्मार्ट कॉन्ट्रैक्ट बग इच्छित व्यावसायिक तर्क और कोड वास्तव में क्या करता है, के बीच बेमेल होने के कारण होते हैं।
स्मार्ट कॉन्ट्रैक्ट यूनिट टेस्टिंग यकीनन स्मार्ट कॉन्ट्रैक्ट के लिए सबसे बुनियादी सुरक्षा उपाय है, लेकिन स्मार्ट कॉन्ट्रैक्ट्स की एक चौंकाने वाली संख्या में या तो उनकी कमी है या अपर्याप्त परीक्षण कवरेज है ।
लेकिन इकाई परीक्षण केवल अनुबंधों के "खुशहाल पथ" (अपेक्षित / डिज़ाइन किए गए व्यवहार) का परीक्षण करने के लिए होते हैं। आश्चर्यजनक मामलों का परीक्षण करने के लिए, अतिरिक्त परीक्षण पद्धतियों को लागू किया जाना चाहिए।
यहां की कुछ कार्यप्रणालियों से अपरिचित लोगों के लिए, साइफ्रिन ऑडिट्स के पैट्रिक कोलिन्स ने अपने वीडियो में स्टेटफुल और स्टेटलेस फ़ज़िंग का एक विनोदी परिचय दिया है।
इन कार्यों को पूरा करने के उपकरण तेजी से अधिक व्यापक और उपयोग में आसान होते जा रहे हैं।
कुछ लेखकों ने इन रिपोज़ में पिछले DeFi हैक की एक सूची तैयार की है:
सिक्योरम का व्यापक रूप से अध्ययन और सुरक्षा का अभ्यास करने के लिए उपयोग किया गया है, लेकिन ध्यान रखें कि रेपो को 2 वर्षों के लिए पर्याप्त रूप से अपडेट नहीं किया गया है
आप हमारे सॉलिडिटी रिडल्स रिपॉजिटरी के साथ सॉलिडिटी कमजोरियों का फायदा उठाने का अभ्यास कर सकते हैं।
DamnVulnerableDeFi एक क्लासिक वॉरगेम है जिसका हर डेवलपर को अभ्यास करना चाहिए
कैप्चर द ईथर और एथरनॉट क्लासिक्स हैं, लेकिन ध्यान रखें कि कुछ समस्याएं अवास्तविक रूप से आसान हैं या पुरानी सॉलिडिटी अवधारणाओं को सिखाती हैं
कुछ प्रतिष्ठित क्राउडसोर्स सुरक्षा फर्मों के पास अध्ययन करने के लिए पिछले ऑडिट की एक उपयोगी सूची है।
यदि आप सॉलिडिटी में धाराप्रवाह नहीं हैं, तो कोई तरीका नहीं है कि आप एथेरियम स्मार्ट कॉन्ट्रैक्ट्स का ऑडिट कर पाएंगे।
स्मार्ट कॉन्ट्रैक्ट ऑडिटर बनने के लिए कोई उद्योग मान्यता प्राप्त प्रमाणन नहीं है। कोई भी एक सॉलिडिटी ऑडिटर होने का दावा करते हुए एक वेबसाइट और सोशल मीडिया प्रोफाइल बना सकता है और सेवाओं की बिक्री शुरू कर सकता है, और कई लोगों ने ऐसा किया है। इसलिए, सावधानी बरतें और किसी को भर्ती करने से पहले रेफरल प्राप्त करें।
एक स्मार्ट कॉन्ट्रैक्ट ऑडिटर बनने के लिए, आपको बग्स का पता लगाने में औसत सॉलिडिटी डेवलपर की तुलना में काफी बेहतर होना चाहिए। इसलिए, ऑडिटर बनने का "रोडमैप" महीनों और महीनों के अथक और जानबूझकर अभ्यास से ज्यादा कुछ नहीं है जब तक कि आप सबसे बेहतर स्मार्ट कॉन्ट्रैक्ट बग पकड़ने वाले नहीं हैं।
यदि आप कमजोरियों की पहचान करने में अपने साथियों से बेहतर प्रदर्शन करने के दृढ़ संकल्प की कमी रखते हैं, तो यह संभावना नहीं है कि आप अत्यधिक प्रशिक्षित और प्रेरित अपराधियों से पहले महत्वपूर्ण मुद्दों को खोज पाएंगे।
स्मार्ट कॉन्ट्रैक्ट ऑडिटिंग को हाल ही में इस धारणा के कारण काम करने के लिए एक वांछनीय क्षेत्र माना गया है कि यह आकर्षक है। वास्तव में, कुछ बग बाउंटी भुगतान 1 मिलियन डॉलर से अधिक हो गए हैं, लेकिन यह अत्यंत दुर्लभ अपवाद है, मानक नहीं।
Code4rena के पास उनकी ऑडिट प्रतियोगिताओं में प्रतिस्पर्धियों से पेआउट का एक सार्वजनिक लीडरबोर्ड है, जो हमें सफलता दर के बारे में कुछ डेटा देता है।
बोर्ड में अभी तक 1171 नाम हैं
इस पर भी विचार करें, जब Openzeppelin ने एक सुरक्षा अनुसंधान फेलोशिप (नौकरी नहीं, एक पूर्व-नौकरी स्क्रीनिंग और प्रशिक्षण) के लिए एक आवेदन खोला, तो उन्हें केवल 10 से कम उम्मीदवारों का चयन करने के लिए 300 से अधिक आवेदन प्राप्त हुए, जिनमें से कुछ को भी पूर्ण प्राप्त होगा समय नौकरी।
यह हार्वर्ड की तुलना में कम प्रवेश दर है।
स्मार्ट कॉन्ट्रैक्ट ऑडिटिंग एक प्रतिस्पर्धी जीरो-सम गेम है। ऑडिट करने के लिए केवल इतनी सारी परियोजनाएँ हैं, सुरक्षा के लिए केवल इतना बजट है, और खोजने के लिए केवल इतने सारे बग हैं। यदि आप अभी सुरक्षा का अध्ययन करना शुरू करते हैं, तो दर्जनों अत्यधिक प्रेरित व्यक्ति और टीमें हैं, जो आप पर भारी बढ़त के साथ हैं। अधिकांश परियोजनाएं एक नए ऑडिटर के बजाय एक प्रतिष्ठा वाले ऑडिटर के लिए प्रीमियम का भुगतान करने को तैयार हैं।
इस लेख में, हमने कमजोरियों की कम से कम 20 विभिन्न श्रेणियों को सूचीबद्ध किया है। यदि आपने प्रत्येक को मास्टर करने में एक सप्ताह बिताया है (जो कुछ हद तक आशावादी है), तो आप केवल यह समझना शुरू कर रहे हैं कि अनुभवी लेखा परीक्षकों के लिए सामान्य ज्ञान क्या है। हमने इस लेख में गैस ऑप्टिमाइज़ेशन या टोकननॉमिक्स को शामिल नहीं किया है, ये दोनों ही एक ऑडिटर के लिए समझने के लिए महत्वपूर्ण विषय हैं। गणित करो और तुम देखोगे कि यह छोटी यात्रा नहीं है।
उस ने कहा, समुदाय आम तौर पर नवागंतुकों के लिए अनुकूल और मददगार है और युक्तियाँ और चालें लाजिमी हैं। लेकिन स्मार्ट अनुबंध सुरक्षा से करियर बनाने की उम्मीद में इस लेख को पढ़ने वालों के लिए, यह स्पष्ट रूप से समझना महत्वपूर्ण है कि एक आकर्षक कैरियर प्राप्त करने की संभावनाएं आपके पक्ष में नहीं हैं। सफलता डिफ़ॉल्ट परिणाम नहीं है।
यह निश्चित रूप से किया जा सकता है , और बहुत से लोग ऑडिटिंग में आकर्षक करियर बनाने के लिए कोई ठोसता नहीं जानने से चले गए हैं। लॉ स्कूल में दाखिला लेने और बार परीक्षा पास करने की तुलना में दो साल की अवधि में स्मार्ट कॉन्ट्रैक्ट ऑडिटर के रूप में नौकरी पाना यकीनन आसान है। यह निश्चित रूप से कई अन्य करियर विकल्पों की तुलना में अधिक उल्टा है।
लेकिन इसके बावजूद आपके सामने तेजी से विकसित हो रहे ज्ञान के पहाड़ पर महारत हासिल करने के लिए और कीड़ों को खोजने के लिए अपने अंतर्ज्ञान को तेज करने के लिए आपकी ओर से अत्यधिक दृढ़ता की आवश्यकता होगी।
यह कहना नहीं है कि स्मार्ट अनुबंध सुरक्षा सीखना एक सार्थक प्रयास नहीं है। यह बिल्कुल है। लेकिन अगर आप अपनी आंखों में डॉलर के निशान के साथ मैदान में आ रहे हैं, तो अपनी उम्मीदों पर काबू रखें।
ज्ञात प्रतिरूपों से अवगत होना महत्वपूर्ण है। हालाँकि, अधिकांश वास्तविक दुनिया बग एप्लिकेशन विशिष्ट हैं। कमजोरियों की किसी भी श्रेणी की पहचान करने के लिए निरंतर और जानबूझकर अभ्यास की आवश्यकता होती है।
हमारे उद्योग-अग्रणी सॉलिडिटी प्रशिक्षण के साथ स्मार्ट अनुबंध सुरक्षा, और कई अन्य एथेरियम विकास विषय सीखें।
इस लेख की मुख्य छवि हैकरनून केएआई इमेज जेनरेटर द्वारा "कंप्यूटर की रक्षा करने वाला एक रोबोट" संकेत के माध्यम से तैयार की गई थी।