আমরা প্রায় সকলেই কিছু কম্পিউটেশনের জন্য ডেটা প্রবেশ করার জন্য গুগল শীট বা মাইক্রোসফ্ট এক্সেল ব্যবহার করেছি। ধরা যাক আপনি কর্মীদের নাম, তাদের ফোন নম্বর, শিরোনাম এবং তারা যে বেতন উপার্জন করতে চান তা লিখতে চান।
এর সহজতম আকারে, শীট বা এক্সেলে একটি রেকর্ড বা কেস দেখতে কেমন হবে:
আপনি বলতে পারেন, কর্মচারীর নাম এবং শিরোনাম উভয়ই পাঠ্য নিয়ে গঠিত যখন ফোন নম্বর এবং বেতন সংখ্যার একটি ক্রম নিয়ে গঠিত।
সুতরাং, একটি শব্দার্থিক দৃষ্টিকোণ থেকে, আমরা মানুষ হিসাবে, বাস্তব জগতে এই ক্ষেত্রগুলির অর্থ কী তা বুঝতে পারি এবং তাদের মধ্যে পার্থক্য করতে পারি।
স্পষ্টতই, পার্থক্য বলার জন্য আপনার কম্পিউটার বিজ্ঞানে ডিগ্রির প্রয়োজন নেই, কীভাবে একজন কম্পাইলার বা দোভাষী এই ডেটা প্রক্রিয়া করে?
এখানেই ডেটা টাইপগুলি আসে এবং এটি এমন কিছু যা প্রোগ্রামাররা তাদের কোড করা প্রোগ্রামিং ভাষার উপর নির্ভর করে নির্দিষ্ট করতে সময় নেয় বা না নেয়।
অন্য কথায়, কর্মচারীর নাম এবং শিরোনামের অধীনে ডেটা পয়েন্টগুলিকে স্ট্রিং বলা হয়। অবশ্যই, বেতন স্পষ্টভাবে একটি পূর্ণসংখ্যা, কোন দশমিক বিন্দু না থাকার কারণে। সহজ কথায়, এগুলি এমন ডেটা প্রকার যা আপনি কোড করার সময় ঘোষণা করতে হবে, যাতে শুধুমাত্র সেই ডেটা টাইপের সাথে যুক্ত সঠিক ক্রিয়াকলাপগুলি সঞ্চালিত হয়।
এইভাবে আমরা সলিডিটিতে একটি পূর্ণসংখ্যা ডেটা টাইপ ঘোষণা করি:
এটি বলেছে, উপরের স্প্রেডশীটে ফোন নম্বর ক্ষেত্রটিতে একটি ডেটা পয়েন্ট রয়েছে যা একটি অনন্য স্ট্রিং হিসাবে ব্যবহার করা হবে, তবে সেই আলোচনাটি অন্য দিনের জন্য। আপাতত, আমাদের ফোকাস হবে আদিম ডেটা টাইপের উপর যা আমরা সবাই মৌলিক পাটিগণিত করেছি।
হ্যাঁ, আমরা পূর্ণসংখ্যার ডেটা টাইপ সম্পর্কে কথা বলছি যেটি মূল গাণিতিক ক্রিয়াকলাপের জন্য গুরুত্বপূর্ণ হলেও যে কোনও গণনার জন্য একটি সীমিত পরিসর রয়েছে।
সম্ভবত, বাস্তব জগতে পূর্ণসংখ্যার ওভারফ্লো সবচেয়ে জনপ্রিয় উদাহরণ যানবাহনে ঘটে। অন্যথায় একটি ওডোমিটার হিসাবে পরিচিত, এই ডিভাইসগুলি সাধারণত ট্র্যাক করে যে একটি গাড়ি কত মাইল ভ্রমণ করেছে।
তাহলে, মাইল ভ্রমণের মান ছয়-সংখ্যার ওডোমিটারে 999999-এর স্বাক্ষরবিহীন পূর্ণসংখ্যার মান পৌঁছালে কী হবে?
আদর্শভাবে, একবার আরেকটি মাইল যোগ করা হলে, এই মানটি 1000000 এ পৌঁছাতে হবে, তাই না? কিন্তু এটি ঘটবে না কারণ সপ্তম সংখ্যার বিধান রয়েছে।
পরিবর্তে, মাইল ভ্রমণের মান 000000 এ পুনরায় সেট করা হয়েছে, যেমনটি নীচে দেখানো হয়েছে:
সংজ্ঞা অনুসারে, যেহেতু সপ্তম সংখ্যাটি উপলব্ধ নয়, তাই সঠিক মান উপস্থাপন করা না হওয়ায় এর ফলে 'ওভারফ্লো' হয়।
আপনি ছবি পেতে, তাই না?
বিপরীতভাবে, বিপরীতটিও ঘটতে পারে যদিও এটি এত সাধারণ না হয়। অন্য কথায়, যখন রেকর্ড করা মান পরিসরে উপলব্ধ সর্বনিম্ন মানের থেকে কম হয় এবং যা অন্যথায় 'আন্ডারফ্লো' নামে পরিচিত।
আমরা সবাই জানি, কম্পিউটার তাদের বাইনারি সমতুল্য হিসাবে মেমরিতে পূর্ণসংখ্যা সংরক্ষণ করবে। এখন, সরলতার জন্য, ধরা যাক যে আপনি একটি 8-বিট রেজিস্টার ব্যবহার করছেন।
= 2⁸*1 + 2⁷*1 + 2⁶*1 + 2⁵*1 + 2⁴*1 + 2³*1 + 2²*1 + 2¹*1 + 2⁰*1
= 256 + 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1
= 111111111
যেখানে প্রতিটি বিট 1, এবং আপনি বলতে পারেন, আপনি উচ্চতর একটি মান সংরক্ষণ করতে পারবেন না।
অন্যদিকে, আপনি যদি 8-বিট রেজিস্টারে 0 নম্বরটি সংরক্ষণ করতে চান তবে এটি দেখতে এইরকম হবে:
= 2⁸*0 + 2⁷*0 + 2⁶*0 + 2⁵*0 + 2⁴*0 + 2³*0 + 2²*0 + 2¹*0 + 2⁰*0
= 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0
= 000000000
যেখানে প্রতিটি বিট 0, যা আপনাকে বলা উচিত যে আপনি একটি কম মান সংরক্ষণ করতে পারবেন না।
অন্য কথায়, এই ধরনের 8-বিট রেজিস্টারের জন্য অনুমোদিত পূর্ণসংখ্যার পরিসর হল 0-511। সুতরাং, এই ধরনের একটি রেজিস্টারে পূর্ণসংখ্যা 512 বা -1 সংরক্ষণ করা কি সম্ভব?
অবশ্যই না. ফলস্বরূপ, আপনি একটি মান সঞ্চয় করবেন যা ওডোমিটার উদাহরণে ভ্রমণ করা মাইলের রিসেট মানের সাথে সাদৃশ্যপূর্ণ, কিন্তু বাইনারি মান হিসাবে।
স্পষ্টতই, এই ধরনের সংখ্যা আরামদায়কভাবে মিটমাট করার জন্য আপনাকে আরও কয়েকটি বিট সহ নিবন্ধন করতে হবে। অন্যথায়, আবার ওভারফ্লো পরিস্থিতির ঝুঁকি।
স্বাক্ষরিত পূর্ণসংখ্যার ক্ষেত্রে, আমরা ঋণাত্মক পূর্ণসংখ্যাও সংরক্ষণ করি। সুতরাং, যখন আমরা একটি সংখ্যা সংরক্ষণ করার চেষ্টা করি যা গৃহীত পরিসরের চেয়ে ছোট, বা শূন্যের চেয়ে কম, যেমন উপরে দেখানো হয়েছে, আন্ডারফ্লো ঘটে।
আবারও, যেহেতু যেকোন গণনা করার বিন্দু হল নির্ধারক ফলাফল পাওয়া, এটি সর্বোত্তমভাবে বিরক্তিকর হতে পারে তবে তাদের সবচেয়ে খারাপভাবে লক্ষ লক্ষ লোকসানের কারণ হতে পারে। বিশেষ করে, যখন এই পূর্ণসংখ্যা ওভারফ্লো বা আন্ডারফ্লো ত্রুটিগুলি স্মার্ট চুক্তিতে ঘটে।
যদিও পূর্ণসংখ্যার ওভারফ্লো এবং আন্ডারফ্লো প্রায় কয়েক দশক ধরে চলে আসছে, স্মার্ট চুক্তিতে একটি বাগ হিসাবে তাদের অস্তিত্ব বাজি ধরেছে। আক্রমণকারীরা যখন এই ধরনের ত্রুটিগুলি ব্যবহার করে, তখন তারা প্রচুর পরিমাণে টোকেনগুলির স্মার্ট চুক্তিকে নিষ্কাশন করতে পারে৷
সম্ভবত প্রথমবার এই ধরনের বাগটি ব্লক 74638 এর সাথে ঘটেছে, যা তিনটি ঠিকানার জন্য কোটি কোটি বিটকয়েন তৈরি করেছে। একটি নরম কাঁটাচামচের মাধ্যমে এই ত্রুটিটি সমাধান করতে কয়েক ঘন্টা সময় লাগবে এবং যা ব্লকটিকে বাতিল করে দেয়, এইভাবে লেনদেনটি অবৈধ হয়ে যায়।
একের জন্য, 21 মিলিয়ন বিটকয়েনের চেয়ে বড় লেনদেন প্রত্যাখ্যান করা হয়েছিল। এটি ওভারফ্লো লেনদেনের জন্য আলাদা ছিল না, অনেকটা যেমন উপরে উল্লিখিত তিনটি অ্যাকাউন্টে এত টাকা পাঠানো হয়েছে৷
যাইহোক, Ethereum স্মার্ট চুক্তিতেও পূর্ণসংখ্যা ওভারফ্লো এবং আন্ডারফ্লো হয়েছে, বিউটিচেইনও একটি বিশিষ্ট উদাহরণ।
এই ক্ষেত্রে, স্মার্ট চুক্তিতে কোডের একটি ত্রুটিপূর্ণ লাইন রয়েছে:
ফলস্বরূপ, আক্রমণকারীরা তাত্ত্বিকভাবে সীমাহীন পরিমাণ BEC টোকেন পেতে সক্ষম হয়েছিল, যা তাত্ত্বিকভাবে (2²⁵⁶)-1 এর মান হতে পারে।
এখন, আসুন একটি স্মার্ট চুক্তির আরেকটি উদাহরণ দেখি যেখানে পূর্ণসংখ্যা আন্ডারফ্লো/ওভারফ্লো হয়।
প্রথম নজরে, দুটি চুক্তি আছে যা এই উদাহরণে ইন্টারঅ্যাক্ট করে এবং যা দেখায় যে পূর্ণসংখ্যা ওভারফ্লো হলে কী ঘটে।
আপনি নীচে দেখতে পাচ্ছেন, চুক্তি টাইমলক, আপনাকে তহবিল জমা এবং উত্তোলনের অনুমতি দেয় তবে একটি পার্থক্য সহ: আপনি শুধুমাত্র একটি নির্দিষ্ট সময়ের পরে পরবর্তীটি সম্পাদন করতে পারবেন। এই ক্ষেত্রে, আপনি শুধুমাত্র এক সপ্তাহের মধ্যে আপনার তহবিল উত্তোলন করতে পারেন।
যাইহোক, একবার আপনি অ্যাটাক কন্ট্রাক্টে অ্যাটাক ফাংশনকে কল করলে, টাইম লক আর কার্যকর হয় না এবং সেই কারণেই আক্রমণকারী ব্যালেন্স অ্যামাউন্ট অবিলম্বে তুলে নিতে পারে।
অন্য কথায়, type(uint).max+1-timeLock.locktime(address(this)) স্টেটমেন্টের সাথে একটি পূর্ণসংখ্যা ওভারফ্লো হওয়ার কারণে, টাইম লকটি বাদ দেওয়া হয়।
উদাহরণস্বরূপ, একবার আপনি উপরের কোডটি ব্যবহার করে উভয় স্মার্ট চুক্তি স্থাপন করার পরে, আপনি টাইমলক ধারণ করে কিনা তা পরীক্ষা করতে পারেন আমানত এবং টাইমলক চুক্তিতে ফাংশন তুলে নেওয়ার মাধ্যমে, নীচে দেখানো হয়েছে:
আপনি দেখতে পাচ্ছেন, 2 ইথারের পরিমাণ নির্বাচন করে, আমরা উপরে দেখানো 2 ইথারের স্মার্ট চুক্তির ব্যালেন্স পাই:
বিশেষত, 2 ইথারের ব্যালেন্স ধারণ করে এমন নির্দিষ্ট ঠিকানাটি ব্যালেন্স ফাংশনের ক্ষেত্রে ঠিকানা যোগ করে এবং ব্যালেন্স বোতামে ক্লিক করে চেক করা যেতে পারে:
যাইহোক, উপরে উল্লিখিত হিসাবে, সময় লক থাকার কারণে আপনি এখনও এই তহবিলগুলি তুলতে পারবেন না। প্রত্যাহারের ফাংশনটি আঘাত করার পরে আপনি যখন কনসোলের দিকে তাকান, তখন আপনি লাল 'x' চিহ্ন দ্বারা নির্দেশিত একটি ত্রুটি খুঁজে পাবেন। আপনি নীচে দেখতে পারেন, এই ত্রুটির কারণ চুক্তি দ্বারা প্রদান করা হয় "লক সময় মেয়াদ শেষ হয়নি":
এখন, মোতায়েন করা অ্যাটাক চুক্তির দিকে নজর দেওয়া যাক, যেমনটি নীচে দেখানো হয়েছে:
এখন, অ্যাটাক ফাংশন চালু করার জন্য, আপনাকে 1 ইথার বা তার বেশি মান জমা করতে হবে। সুতরাং, এই উদাহরণে, আমরা 2 ইথার নির্বাচন করেছি, যেমনটি নীচে দেখানো হয়েছে:
এর পর 'আক্রমণ' মারুন। নিচের 2 ইথারের ব্যালেন্স দ্বারা প্রমাণিত হিসাবে আপনি যে 2 ইথার জমা করেছেন তা অবিলম্বে প্রত্যাহার করা হবে এবং অ্যাটাক চুক্তিতে যোগ করা হবে:
স্পষ্টতই, এটি হওয়ার কথা নয় কারণ আপনি আমানত করার সাথে সাথেই দীর্ঘ সময়ের লক কার্যকর হওয়া উচিত। অবশ্যই, যেমন আমরা জানি type(uint).max+1-timeLock.locktime(address(this)) স্টেটমেন্ট বৃদ্ধি লকটাইম ফাংশন ব্যবহার করে লক টাইম হ্রাস করে। এই কারণেই আমরা অবিলম্বে ইথার ব্যালেন্স প্রত্যাহার করতে সক্ষম হয়েছি।
যা আমাদের সুস্পষ্ট প্রশ্নে নিয়ে আসে: পূর্ণসংখ্যা ওভারফ্লো এবং আন্ডারফ্লো দুর্বলতা ঠিক করার উপায় আছে কি?
পূর্ণসংখ্যা ওভারফ্লো/আন্ডারফ্লো দুর্বলতা ধ্বংসাত্মক হতে পারে তা স্বীকার করে, এই বাগটির কয়েকটি সংশোধন করা হয়েছে। আসুন এই উভয় সংশোধনগুলি দেখুন এবং কীভাবে তারা এই জাতীয় ত্রুটির চারপাশে কাজ করে:
ওপেন জেপেলিন, একটি সংস্থা হিসাবে, সাইবার নিরাপত্তা প্রযুক্তি এবং পরিষেবার ক্ষেত্রে অনেক কিছু অফার করে, যেখানে সেফম্যাথ লাইব্রেরি তার স্মার্ট চুক্তি উন্নয়ন ভান্ডারের একটি অংশ। এই রেপোতে এমন চুক্তি রয়েছে যা আপনার স্মার্ট কন্ট্রাক্ট কোডে আমদানি করা যেতে পারে, SafeMath লাইব্রেরি তাদের মধ্যে একটি।
আসুন দেখি কিভাবে SafeMath.sol-এর মধ্যে একটি ফাংশন পূর্ণসংখ্যা ওভারফ্লো পরীক্ষা করে:
এখন, একবার a+b এর গণনা হয়ে গেলে, c<a হয় কিনা তা পরীক্ষা করে দেখুন। অবশ্যই, এটি শুধুমাত্র একটি পূর্ণসংখ্যা ওভারফ্লো ক্ষেত্রে সত্য হবে।
সলিডিটির কম্পাইলার সংস্করণ 0.8.0 এবং তার উপরে পৌঁছানোর সাথে, পূর্ণসংখ্যার ওভারফ্লো এবং আন্ডারফ্লো জন্য চেকগুলি এখন তৈরি করা হয়েছে৷ তাই, ভাষা এবং এই লাইব্রেরি উভয়ই ব্যবহার করার সময় কেউ এই দুর্বলতা পরীক্ষা করতে এখনও এই লাইব্রেরিটি ব্যবহার করতে পারে৷ অবশ্যই, যদি আপনার স্মার্ট চুক্তির জন্য 0.8.+ এর কম একটি কম্পাইলার সংস্করণের প্রয়োজন হয়, তাহলে আপনাকে ওভারফ্লো বা আন্ডারফ্লো এড়াতে এই লাইব্রেরিটি ব্যবহার করতে হবে।
এখন, যেমন আগে উল্লিখিত হয়েছে, যদি আপনার স্মার্ট চুক্তির জন্য, আপনি 0.8.0 এবং তার উপরে একটি কম্পাইলার সংস্করণ ব্যবহার করছেন, এই সংস্করণে এই ধরনের দুর্বলতার জন্য একটি অন্তর্নির্মিত চেকার রয়েছে।
প্রকৃতপক্ষে, এটি উপরের স্মার্ট চুক্তির সাথে কাজ করে কিনা তা যাচাই করার জন্য, যখন কম্পাইলার সংস্করণটিকে "^0.8.0" এ পরিবর্তন করা হয় এবং এটি পুনরায় স্থাপন করা হয়, তখন নিম্নলিখিত 'রিভার্ট' ত্রুটিটি পাওয়া যায়:
অবশ্যই, 2 ইথারের কোন জমা করা হয় না, যা টাইম লক মানের ওভারফ্লো চেক করার কারণে। ফলস্বরূপ, প্রথম স্থানে কোন তহবিল জমা না থাকার কারণে কোন উত্তোলন সম্ভব নয়।
নিঃসন্দেহে, Attack.attack() ফাংশন কল এখানে কাজ করেনি, তাই সবকিছুই ভালো!
এই দীর্ঘ ব্লগ পোস্ট থেকে আপনার যদি কিছু সংগ্রহ করা উচিত, তা হল এই দুর্বলতা উপেক্ষা করা, যেমন BEC আক্রমণ থেকে, ব্যয়বহুল প্রমাণিত হতে পারে। যেমন আপনি বলতে পারেন, যদি চেক না করা থাকে, তাহলে অ-দূষিত ত্রুটিগুলি ঘটতে পারে। অথবা হ্যাকারদের জন্য এই দুর্বলতা কাজে লাগানো সহজ।
যার কথা বলা, এবং BEC আক্রমণ কীভাবে হয়েছিল সে সম্পর্কে আমাদের উপলব্ধি ব্যবহার করে, এই দুর্বলতাকে স্বীকৃতি দেওয়া আপনার স্মার্ট চুক্তিগুলি লেখার সময় যেকোনও আক্রমণ প্রতিরোধে একটি দীর্ঘ পথ যেতে পারে, অফারে সংশোধন করার জন্য ধন্যবাদ। এমনকি যদি আরও কয়েকটি স্মার্ট চুক্তির দুর্বলতা থাকে যা আপনাকে ট্রিপ করার অপেক্ষায় থাকে।