حمله Second Preimage در درخت مرکل سالیدیتی

در ساختار درخت مرکل، زمانی که یک نود میانی به جای یک برگ به سیستم معرفی می‌شود، احتمال بروز حمله‌ای به نام Second Preimage وجود دارد.

نام این حمله ممکن است گمراه‌کننده باشد، زیرا به‌اشتباه این تصور را ایجاد می‌کند که تابع هش مورد استفاده می‌تواند چندین تصویر قابل محاسبه داشته باشد. اما در واقع، توابع هش مدرن چنین ضعفی ندارند و امکان یافتن دو ورودی متفاوت با هش یکسان، عملاً وجود ندارد.

نام دقیق‌تری که برای این نوع حمله می‌توان به‌کار برد، “حمله نود به‌جای برگ” یا “حمله اثبات کوتاه‌شده” است. این نام‌ها به‌درستی نشان می‌دهند که مهاجم چگونه با ارائه یک نود میانی به‌عنوان برگ، فرآیند تأیید را دور می‌زند.

پیش‌نیازها

در این مقاله، فرض می‌کنیم خواننده با مفاهیم درخت مرکل و اثبات مرکل آشنا است.

نمادگذاری

در اینجا، h(x) نشان‌دهنده هش مقدار x است و h(x + y) هش ترکیب مقادیر x و y را نشان می‌دهد. ما از تابع keccak256 به‌عنوان مرجع استفاده می‌کنیم تا مفاهیم بعدی را ساده‌تر بیان کنیم. البته تمام ایده‌های مطرح‌شده برای سایر توابع هش نیز قابل اجرا هستند.

برای نمایش برگ‌های درخت مرکل از نماد ℓ استفاده می‌کنیم و برگ شماره i را با ℓᵢ نشان می‌دهیم.

نمونه‌ای از یک حمله Second Preimage در درخت مرکل سالیدیتی

فرض کنید می‌خواهیم برای برگ شماره ۲ (ℓ₂) درخت مرکل زیر، یک اثبات (Merkle Proof) ایجاد کنیم. در این حالت، مقدار برگ ℓ₂ است و مسیر اثبات شامل مقادیر [h(ℓ₁), h(b), h(f)] خواهد بود. مقادیر a، b، c تا g همگی حاصل ترکیب مقادیر فرزندان خود هستند (یعنی a برابر است با h(ℓ₁ + ℓ₂) و به همین ترتیب).

در اینجا، زمانی اثبات را معتبر تلقی می‌کنیم که مقدار نهایی h(g) برابر با ریشه مرکل (Merkle Root) باشد.

درخت مرکل و اثبات مرکل

در این سناریو، مسیر اثباتی که برای ℓ₂ ارائه می‌دهیم شامل سه مقدار [h(ℓ₁), h(b), h(f)] است که در نمودار با رنگ سبز مشخص شده‌اند. این سه مقدار برای بازسازی مسیر تا ریشه استفاده می‌شوند:

در نهایت اگر h(g) با مقدار ریشه درخت مرکل برابر باشد، اثبات پذیرفته می‌شود:

حمله Second Preimage چگونه کار می‌کند؟

فرض کنید مهاجم به‌جای یک برگ واقعی، مقدار a را به‌عنوان برگ ارائه دهد و مسیر اثبات را نیز برابر با [h(b), h(f)] قرار دهد.

حمله Second Preimage

در این حالت، قرارداد هوشمند مقدار a را به‌عنوان برگ دریافت می‌کند، در حالی که a = h(h(ℓ₁), h(ℓ₂)) است. یعنی a در واقع یک نود میانی از درخت مرکل است، نه یک برگ واقعی.

نکته کلیدی اینجاست: اگر یک اثبات مرکل معتبر باشد، مهاجم می‌تواند نسخه کوتاه‌تری از همان اثبات را هم معتبر جلوه دهد، به‌شرط آن‌که مقدار اول مسیر اثبات (که در اصل بخشی از نود والد بوده) را به‌جای برگ اصلی به قرارداد بدهد.

به عبارت دیگر، مهاجم با ارائه یک نود میانی به‌عنوان برگ، و استفاده از بخشی از اثبات قبلی، موفق می‌شود سیستم را فریب دهد.

در نتیجه، قرارداد هوشمند اثباتی را تأیید می‌کند که برگ آن اصلاً در درخت مرکل اصلی وجود ندارد!

چطور می‌توانیم جلوی این حمله را بگیریم؟

هشدار OpenZeppelin درباره حمله Second Preimage

کتابخانه درخت مرکل (Merkle Tree) در مجموعه ابزارهای OpenZeppelin، به‌درستی در بخش توضیحات (کامنت ها) نسبت به این حمله هشدار داده است. این هشدار همراه با چند راهکار پیشنهادی ارائه شده که هدف آن‌ها جلوگیری از اجرای موفقیت‌آمیز چنین حمله‌ای در قراردادهای هوشمند است.

هشدار OpenZeppelin

در ادامه، به بررسی دقیق این راهکارها می‌پردازیم و توضیح می‌دهیم که چگونه هر یک از آن‌ها می‌توانند از وقوع این نوع حمله جلوگیری کنند.

این حمله به برگ‌های ۶۴ بایتی نیاز دارد

برای اجرای موفقیت‌آمیز این نوع حمله، مهاجم باید تصویر اولیه (preimage) یک نود میانی را به‌عنوان برگ به قرارداد ارائه دهد. نه صرفاً هش آن نود. به‌عبارت دقیق‌تر، مهاجم باید مقدار a را به‌عنوان برگ وارد کند، نه h(a).

از آن‌جا که در سالیدیتی هش ها به‌صورت پیش‌فرض ۳۲ بایتی هستند، مقدار a = h(ℓ₁) + h(ℓ₂) یک رشته ۶۴ بایتی خواهد بود (ترکیب دو هش ۳۲ بایتی). بنابراین اگر قرارداد هوشمند ورودی‌های ۶۴ بایتی را به‌عنوان برگ نپذیرد، این حمله قابل اجرا نخواهد بود.

در نتیجه اگر طول ورودی‌های برگ متفاوت از ۶۴ بایت باشد، مهاجم نمی‌تواند مقدار h(ℓ₁) + h(ℓ₂) را به‌عنوان یک برگ جعلی وارد کند و در نتیجه امکان اجرای حمله از بین می‌رود.

استفاده از تابع هش متفاوت برای برگ ها به‌عنوان راهکار دفاعی

در برخی کاربردها، ممکن است نیاز داشته باشیم که قرارداد برگ های ۶۴ بایتی را بپذیرد. در این شرایط، نمی‌توان صرفاً با محدود کردن طول ورودی، جلوی حمله را گرفت. اما همچنان راهی برای مقابله وجود دارد: استفاده از تابع هش متفاوت برای برگ ها نسبت به آنچه در مسیر اثبات و محاسبه ریشه استفاده می‌شود.

به‌عبارت ساده‌تر، زمانی که برگ ها را هش می‌کنیم، از تابعی مانند h' استفاده می‌کنیم، در حالی که برای بقیه نودهای درخت از تابع معمول h استفاده می‌کنیم. این کار باعث می‌شود مهاجم نتواند یک نود میانی را با عبور از همان مسیر هش، بازسازی و به‌عنوان برگ جعلی معرفی کند.

برای مثال، مهاجم قصد دارد مقدار a = h(h(ℓ₁), h(ℓ₂)) را به‌عنوان برگ وارد کند تا اثبات تقلبی را ایجاد کند. اما اگر سیستم، برگ‌ها را ابتدا از طریق تابع دیگری مانند h'(x) هش کرده باشد، آن‌گاه مهاجم دیگر نمی‌تواند از مسیر اصلی h(a) برای ساختن این برگ استفاده کند. چون هش اولیه برگ متفاوت خواهد بود.

استفاده OpenZeppelin از هش دوتایی به‌عنوان “تابع هش متفاوت”

کتابخانه OpenZeppelin برای تفکیک بین هش برگ‌ها و نودهای میانی، به‌جای استفاده از تابع هش متفاوت و پرهزینه (مانند توابع پیش‌کامپایل‌شده)، از یک راهکار ساده و مؤثر بهره می‌برد: اعمال هش دوباره روی داده برگ. به‌عبارت دیگر، اگر تابع هش معمول را h(x) در نظر بگیریم، آنگاه هش برگ‌ها به‌صورت h'(x) = h(h(x)) محاسبه می‌شود.

ما با زیرخط های سبز مشخص کرده‌ایم که در چه بخش‌هایی، هش دوبار روی داده اولیه اعمال شده تا برگ نهایی در درخت ساخته شود.

استفاده OpenZeppelin از هش دوتایی

توجه داشته باشید که این کتابخانه شما را ملزم نمی‌کند که حتماً هش را دوبار اعمال کنید. در واقع حتی شما را مجبور به هش‌کردن برگ ها هم نمی‌کند! اما اگر برگ ها را اصلاً هش نکنید، ممکن است مسیر حمله هایی فراتر از حمله second preimage باز شود.

جمع بندی

حمله Second Preimage در درخت مرکل سالدیتی زمانی قابل اجرا است که بتوان نسخه کوتاه‌شده‌ای از یک اثبات معتبر را ارائه داد و همچنان ریشه مرکل (Merkle Root) را بازسازی کرد. درخت مرکل فقط یک تصویر اولیه (preimage) دارد، اما این ریشه به‌صورت مرحله‌ای و تدریجی ساخته می‌شود، نه از طریق هش‌کردن کل مسیر اثبات به‌صورت یکجا. به همین دلیل، اگر اثبات را از نقطه‌ای جلوتر در دنباله آغاز کنیم، ممکن است بتوانیم ورودی متفاوتی ارائه دهیم که همان ریشه را تولید می‌کند. درک دقیق این ساختار برای توسعه قراردادهای ایمن ضروری است. به ویژه افرادی که در مسیر آموزش برنامه نویسی بلاکچین و امنیت آن فعالیت می‌کنند.

خبر خوب این است که مقابله با این حمله بسیار ساده است: کافی است اطمینان حاصل کنیم که هیچ مقدار میانی در درخت مرکل به‌اشتباه به‌عنوان یک برگ (leaf) تفسیر نشود. به‌عبارت دیگر، تنها مقادیری که واقعاً برگ هستند باید به‌عنوان برگ معتبر در ورودی پذیرفته شوند.

به این مطلب امتیاز دهید

راستی! برای دریافت مطالب جدید در کانال تلگرام یا پیج اینستاگرام سورس باران عضو شوید.

دوره آموزش طراحی وب سایت مدرسه با PHP و MySql
  • انتشار: ۶ مرداد ۱۴۰۴

دسته بندی موضوعات

آخرین محصولات فروشگاه

مشاهده همه

نظرات

بازخوردهای خود را برای ما ارسال کنید