SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-21

بیلد‌های بازتولیدپذیر واقعا چه چیزی را اثبات می‌کنند؟

یک checksum ثابت می‌کند فایل سالم رسیده است، اما بیلد بازتولیدپذیر تایید می‌کند باینری دقیقاً از سورس‌کد شما ساخته شده است. تفاوت این دو مفهوم امنیتی را اینجا بخوانید.

اثبات بیلد‌های بازتولیدپذیر

بیلد‌های بازتولیدپذیر (Reproducible builds) تنها یک حقیقت محدود را اثبات می‌کنند: باینری که به دست شما رسیده، همان باینری است که دقیقاً از همین سورس‌کد تولید شده است. هر کسی می‌تواند همان سورس را بگیرد، دوباره آن را بیلد کند و بایت‌ها را با هم مقایسه نماید. با این کار، تأیید صحت دیگر عملی نیست که فقط ناشر قادر به انجام آن باشد.

پروژه Reproducible Builds آن را این‌گونه تعریف می‌کند: «یک بیلد زمانی بازتولیدپذیر است که با داشتن سورس‌کد، محیط بیلد و دستورالعمل‌های بیلد یکسان، هر طرفی بتواند کپی‌های بیت‌به‌بیت یکسانی از تمام آرتیفکت‌های مشخص‌شده ایجاد کند.» خودِ عمل مقایسه، یک هش (hash) است. تمام دشواری کار در این است که محیط و دستورالعمل‌ها را به اندازه‌ای دقیق و ثابت (pin) کنیم که دو ماشین متفاوت به نتیجه‌ای یکسان برسند.

چرا checksum به این پرسش پاسخ نمی‌دهد

یک checksum منتشرشده ثابت می‌کند که فایل بدون خرابی به مقصد رسیده است. امضای دیجیتال روی آن checksum ثابت می‌کند که فایل از طرف دارندهٔ کلید ارسال شده است. هیچ‌کدام از این دو مورد، دربارهٔ آنچه پیش از ایجاد فایل (artifact) رخ داده است، اطلاعاتی نمی‌دهند. اگر ماشین build ناشر آلوده شده باشد، فایل باینری مخرب دقیقاً مانند یک فایل سالم checksum و امضا می‌شود، بنابراین تمام بررسی‌های بعدی با موفقیت انجام می‌شوند. اگر نگهدارندهٔ پروژه، فایل را از یک working tree که هرگز push نشده است build کند، همین وضعیت صادق است.

این همان شکاف امنیتی است. شما می‌توانید سورس‌کد را بخوانید، امضا را تأیید کنید، checksum را چک کنید و همچنان کدی را اجرا کنید که هرگز در مخزن (repository) وجود نداشته است. بنابراین، قابلیت بازتولید (reproducibility) موضوعی متفاوت از تأیید دانلود بر اساس checksum منتشرشده است. checksum از انتقال فایل محافظت می‌کند، اما بازسازی (rebuild) از تمام مراحلی که پیش از انتقال رخ داده است، محافظت می‌کند.

این حمله صرفاً تئوری نیست. نفوذ به SolarWinds Orion در سال 2020 دقیقاً در همین موقعیت قرار داشت: سیستم build، فایل‌های امضاشده‌ای را تولید کرد که با سورس‌کدی که توسط دیگران بازبینی شده بود، مطابقت نداشت. تمام بررسی‌های امضا با موفقیت انجام شد، زیرا امضاها از مرحلهٔ تولید فایل (artifact) آغاز می‌شوند.

آنچه یک بیلد تکرارپذیر اثبات نمی‌کند

این بخشی است که در مورد آن اغراق می‌شود، بنابراین در مورد محدودیت‌ها دقیق باشید.

  • این به معنای امن بودن سورس‌کد نیست. یک در پشتی (backdoor) که به‌صورت عمومی کامیت شده باشد، به‌طور تکرارپذیر بیلد می‌شود و هر بازسازی‌کننده‌ای آن را تأیید می‌کند، زیرا همه آن‌ها همان سورس‌کد مخرب را بیلد کرده‌اند. تکرارپذیری، هدف را به درخت سورس‌کد منتقل می‌کند. همچنان شخصی باید آن درخت را بررسی کند، به همین دلیل است که سیاست بازبینی، از جمله سیاست‌های مربوط به کد تولیدشده توسط هوش مصنوعی در پروژه‌های متن‌باز، یک کنترل مجزا باقی می‌ماند.
  • این به معنای امن بودن ورودی‌های شما نیست. وابستگی‌ها بخشی از چیزی هستند که شما بیلد می‌کنید. یک پکیج مخرب که در زمان بیلد فراخوانی می‌شود، در آرتیفکت نهایی کامپایل می‌گردد و هر بازسازی‌کننده‌ای که همان وابستگی را فراخوانی کند، با شما هم‌نظر خواهد بود. این همان روشی است که یک حمله به زنجیره تأمین npm به سرور می‌رسد و یک بیلد تکرارپذیر، آن را با دقت بازتولید می‌کند.
  • این به معنای صادق بودن زنجیره ابزار (toolchain) نیست. اگر کامپایلر آلوده باشد، هر بازسازی‌کننده‌ای که از آن کامپایلر استفاده کند، همان خروجی آلوده را تولید می‌کند و همه نتایج با هم مطابقت خواهند داشت. تکرارپذیری هزینه این حمله را افزایش می‌دهد، اما آن را شناسایی نمی‌کند.
  • این هیچ چیزی درباره آسیب‌پذیری‌ها نمی‌گوید. یک کتابخانه قدیمی که بیت‌به‌بیت بازتولید شده است، همچنان همان کتابخانه قدیمی با نقص‌های منتشرشده‌اش است، بنابراین بررسی سرور برای CVEهای شناخته‌شده را طبق برنامه زمانی خود ادامه دهید.

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

چرا یک منبع یکسان، بایت‌های متفاوتی تولید می‌کند

بیشتر نرم‌افزارها به‌صورت پیش‌فرض بازتولیدپذیر (reproducible) نیستند و دلایل آن ساده و خسته‌کننده است. کامپایلرها و فرمت‌های آرشیو، اطلاعاتی درباره ماشینی که آن‌ها را اجرا کرده است، ثبت می‌کنند.

  • برچسب زمانی (Timestamp). فرمت‌های tar، ar و zip زمان آخرین تغییر فایل را ذخیره می‌کنند، بنابراین ساختن فایل در ثانیه‌ای متفاوت، خروجی را تغییر می‌دهد.
  • مسیر (Path). اطلاعات دیباگ، مسیر مطلق دایرکتوری ساخت را ثبت می‌کنند؛ بنابراین فایلی که در /home/alice/src ساخته شده با فایلی که در /build/pkg ساخته شده متفاوت است، حتی اگر کد منبع کاملاً یکسان باشد.
  • ترتیب (Ordering). خواندن یک دایرکتوری، ورودی‌ها را بر اساس ترتیب فایل‌سیستم برمی‌گرداند، بنابراین ترتیب لینک‌دهی یا ترتیب اعضای آرشیو بین ماشین‌های مختلف تغییر می‌کند.
  • هویت (Identity). اسکریپت‌های ساخت، نام کاربری، نام میزبان (hostname) یا تنظیمات محلی (locale) شخصی که فایل را ساخته است، در آن درج می‌کنند.
  • تصمیمات زمان ساخت (Build-time decisions). تشخیص ویژگی‌های CPU یا استفاده از مقادیر تصادفی (seeding)، باعث می‌شود خروجی به‌جای منبع، به ماشین وابسته باشد.

شما می‌توانید وقوع دلیل اول را در حدود ده ثانیه مشاهده کنید:

mkdir -p /tmp/rb && cd /tmp/rb
echo hello > a.txt && tar cf one.tar a.txt
sleep 2
echo hello > a.txt && tar cf two.tar a.txt
sha256sum one.tar two.tar

دو هش تولید شده متفاوت هستند زیرا هدر tar زمان تغییر فایل a.txt را ذخیره می‌کند و بازنویسی فایل، آن زمان را دو ثانیه جلو برده است. محتوای فایل بایت به بایت یکسان است. ثابت کردن (Pinning) متادیتا این مشکل را حل می‌کند:

export SOURCE_DATE_EPOCH=1700000000
tar --sort=name --mtime="@${SOURCE_DATE_EPOCH}" --owner=0 --group=0 --numeric-owner -cf three.tar a.txt
touch a.txt
tar --sort=name --mtime="@${SOURCE_DATE_EPOCH}" --owner=0 --group=0 --numeric-owner -cf four.tar a.txt
sha256sum three.tar four.tar

اکنون هش‌ها مطابقت دارند، زیرا هیچ فیلدی در هدر آرشیو از وضعیت فعلی ماشین تأثیر نمی‌گیرد. --sort=name ترتیب را اصلاح می‌کند، --mtime ساعت را ثابت نگه می‌دارد و فلگ‌های مالکیت مانع از ثبت شناسه کاربری (user id) شما می‌شوند.

بررسی تفاوت‌ها با diffoscope

هنگامی که دو بیلد با هم تفاوت دارند، sha256sum فقط به شما می‌گوید که تفاوت وجود دارد و هیچ اطلاعات دیگری ارائه نمی‌دهد. ابزار diffoscope برای این ساخته شده است که دلیل تفاوت را به زبانی که برای انسان قابل خواندن باشد، توضیح دهد. این ابزار هر دو سمت را به‌صورت بازگشتی باز می‌کند، فرمت‌های باینری را به متن تبدیل کرده و تفاوت‌های متنی را استخراج می‌کند. این ابزار از بسته‌های Debian، فایل‌های باینری ELF، آرشیوهای tar و ZIP، فایل‌های PDF، پایگاه‌داده‌های SQLite و بیش از صد فرمت دیگر پشتیبانی می‌کند.

sudo apt install -y diffoscope
diffoscope one.tar two.tar

برای جفت فایل tar در بالا، گزارش کوتاه است. اگر بخش‌های اضافی آن را حذف کنیم، به این شکل دیده می‌شود:

--- one.tar
+++ two.tar
├── file list
│ @@ -1 +1 @@
│ -rw-r--r-- 0/0  6 2026-08-18 10:14:02.000000 a.txt
│ +rw-r--r-- 0/0  6 2026-08-18 10:14:04.000000 a.txt

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

diffoscope --html report.html build1.changes build2.changes

ابزار diffoscope در صورت یکسان بودن ورودی‌ها با کد 0، در صورت وجود تفاوت با کد 1 و در صورت بروز مشکل با کد 2 خارج می‌شود؛ بنابراین بدون نیاز به اسکریپت واسط، مستقیماً در کارهای CI قابل استفاده است. روی یک VPS کوچک، به جای diffoscope، بسته diffoscope-minimal را نصب کنید: بسته کامل، مجموعه‌ای بزرگ از ابزارهای کمکی برای فرمت‌های مختلف را نصب می‌کند که احتمالاً هرگز به آن‌ها نیاز نخواهید داشت.

متغیر SOURCE_DATE_EPOCH چه چیزی را اصلاح می‌کند و محدودیت آن کجاست

متغیر محیطی SOURCE_DATE_EPOCH شامل یک عدد است: زمان آخرین تغییر در سورس‌کد، که بر حسب ثانیه از تاریخ 1 ژانویه 1970 به وقت UTC محاسبه می‌شود. ابزار بیلد (build tool) که از این متغیر پشتیبانی می‌کند، هر جا که قرار باشد زمان فعلی را از سیستم‌عامل بپرسد، از این عدد استفاده می‌کند. این متغیر را از طریق سیستم کنترل نسخه (version control) تنظیم کنید تا مقدار آن به جای زمان بیلد، با سورس‌کد همراه باشد:

export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)

در بسته‌های Debian، ابزار debhelper این مقدار را به‌طور خودکار از فایل changelog استخراج می‌کند. تنظیم دستی آن در debian/rules به این صورت است:

export SOURCE_DATE_EPOCH ?= $(shell dpkg-parsechangelog -STimestamp)

پشتیبانی از این متغیر به هر ابزار بستگی دارد و سراسری نیست. cmake نسخه 3.8 و جدیدتر، gcc نسخه 7 و جدیدتر، rpm نسخه 4.13 به بالا و Docker buildx نسخه 0.10 و جدیدتر همگی آن را می‌خوانند. اسکریپت‌های شخصی شما تا زمانی که برای خواندن آن برنامه‌نویسی نشوند، از آن استفاده نمی‌کنند. اگر اسکریپتی از date استفاده می‌کند، متغیر را به آن تزریق کنید:

BUILD_DATE="$(date --utc --date="@${SOURCE_DATE_EPOCH:-$(date +%s)}" +%Y-%m-%d)"

هنگام پیاده‌سازی، یک قانون اهمیت دارد. اگر متغیر از قبل تنظیم شده باشد، همان مقدار «زمان فعلی» برای بیلد شما محسوب می‌شود؛ بنابراین هرگز مقداری که فراخواننده (caller) تعیین کرده است را بازنویسی نکنید.

ایمیج‌های کانتینر نیز همین مشکل را در قالبی متفاوت دارند. Docker buildx نسخه 0.10 و جدیدتر، متغیر SOURCE_DATE_EPOCH را از shell شما به عنوان یک آرگومان بیلد به داخل فرآیند می‌فرستد. برای اینکه timestamp فایل‌های داخل لایه‌ها اصلاح شود، اکسپورتر باید آن‌ها را بازنویسی کند؛ قابلیتی که در BuildKit نسخه 0.13 اضافه شد و روش مستند آن، نتیجه را به یک رجیستری ارسال می‌کند:

export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
docker buildx build --output type=image,name=registry.example.com/app:1.0,push=true,rewrite-timestamp=true .

تست بیلد خود با استفاده از reprotest

ابزار reprotest سورس یکسان را دو بار بیلد می‌کند و در هر بار، محیط بیلد را به‌صورت هدفمند تغییر می‌دهد تا نتایج را با هم مقایسه کند. هدف اصلی این ابزار، همین تغییرات (variations) است. به‌صورت پیش‌فرض، این ابزار مسیر بیلد، زمان، منطقه زمانی، locale، umask، نام میزبان (hostname)، کاربر و گروه، تعداد CPUها، دایرکتوری home و ترتیب فایل‌ها را تغییر می‌دهد.

sudo apt install -y reprotest
reprotest . -- null

هر چیزی که بعد از -- می‌آید، backend محیط بیلد را انتخاب می‌کند و null به معنای سیستمی است که هم‌اکنون روی آن هستید. برای نگهداری دایرکتوری‌های موقت جهت بررسی، از -vv -d استفاده کنید، مانند نمونه در reprotest . -vv -- null -d. از reprotest auto -- null استفاده کنید تا ابزار خودش نوع درخت سورس را تشخیص دهد.

برخی از تغییرات نیاز به دسترسی‌های ویژه یا بسته‌های اضافی دارند و در صورت عدم اجرا، با خطا متوقف می‌شوند. به‌جای اجرای کل فرآیند با کاربر root، این موارد را غیرفعال کنید:

reprotest --vary=-user_group,-domain_host,-fileordering auto -- null

هر موردی که reprotest گزارش می‌دهد، چیزی است که یک rebuilder بعداً به‌صورت عمومی و با نام پروژه شما گزارش خواهد کرد.

معنای نتیجه بازسازی‌کننده (rebuilder)

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

دبیان محیط ساخت را در فایلی با نام .buildinfo ثبت می‌کند که dpkg-buildpackage آن را در کنار .deb می‌نویسد. فیلدها بخش جالب ماجرا هستند. Installed-Build-Depends تمام بسته‌های نصب‌شده‌ای که می‌توانند بر ساخت تأثیر بگذارند را با نسخه‌های دقیق فهرست می‌کند. Build-Path محل اجرای ساخت را ثبت می‌کند. Environment متغیرهای محیطی که اهمیت آن‌ها مشخص است را ثبت می‌کند. Checksums-Sha256 خروجی‌ها را پوشش می‌دهد. آن فایل دستورالعمل یک تلاش دوم است:

sudo apt install -y devscripts mmdebstrap
debrebuild --buildresult=./artifacts --builder=mmdebstrap hello_2.10-2_amd64.buildinfo

debrebuild فایل buildinfo را می‌خواند و نسخه‌های دقیق وابستگی‌های ذکر شده در آن را از snapshot.debian.org دریافت می‌کند، بنابراین بازسازی امروز می‌تواند از نسخه‌های بسته‌ای استفاده کند که در روز ساخت اصلی وجود داشته‌اند. سازنده mmdebstrap نیازی به تنظیم chroot یا دسترسی superuser ندارد. آرتیفکت‌های تولیدشده توسط آن را با استفاده از diffoscope با نسخه آرشیو مقایسه کنید.

آرچ لینوکس از rebuilderd استفاده می‌کند که این کار را به‌طور مداوم انجام داده و نتایج را منتشر می‌کند:

rebuildctl -H https://reproducible.archlinux.org pkgs ls --name rebuilderd

وضعیت‌ها شامل GOOD، BAD و UNKWN هستند و برداشت ساده از هر کدام در جهتی متفاوت اشتباه است. وضعیت GOOD به این معناست که یک طرف مستقل به همان بایت‌ها دست یافته است؛ این یک اظهارنظر قوی درباره فرآیند ساخت است، اما هیچ چیزی درباره سورس نمی‌گوید. وضعیت BAD تقریباً هرگز به معنای حمله نیست: دلیل معمول آن یک timestamp یا مسیری است که بسته‌بندی در ثابت‌سازی آن شکست خورده است، به همین دلیل است که rebuilderd می‌تواند یک گزارش diffoscope را به خطا پیوست کند. وضعیت UNKWN به این معناست که هیچ‌کس آن را تست نکرده است و یک بسته تست‌نشده، یک بسته تأییدشده نیست.

بنابراین قانون عملیاتی کوتاه است. نتیجه BAD دلیلی برای خواندن گزارش است. اگر گزارش نشان‌دهنده timestampها، مسیرهای ساخت یا ترتیب اعضا باشد، یک باگ بسته‌بندی ثبت کنید. اگر گزارش نشان‌دهنده کدهای اجرایی متفاوت بدون هیچ توضیح مشابهی است، استقرار آن بیلد را متوقف کرده و موضوع را ارجاع دهید.

میزان بازتولیدپذیری (reproducibility) دبیان در حال حاضر چقدر است؟

ChartDebian packages reproducible in the test framework, amd64
The data behind this chart
[
  {
    "label": "unstable",
    "percent_reproducible": 94.2,
    "tested_count": "41,163"
  },
  {
    "label": "forky",
    "percent_reproducible": 93.4,
    "tested_count": "39,059"
  },
  {
    "label": "experimental",
    "percent_reproducible": 67.0,
    "tested_count": "588"
  }
]

در روز نگارش این مطلب، وضعیت شاخه unstable برای معماری amd64 نشان‌دهنده 94.2% بازتولیدپذیری در میان 41,163 بستهٔ آزمایش‌شده بود. شاخه experimental در سطح 67.0% قرار داشت که مربوط به نمونه‌ای بسیار کوچک‌تر و جدیدتر شامل 588 بسته است؛ این نتیجه‌ای است که برای بسته‌هایی که هنوز کسی اصلاح آن‌ها را به پایان نرسانده، انتظار می‌رود.

این ارقام از صفحه دبیان در tests.reproducible-builds.org استخراج شده‌اند که در تاریخ 2026-08-18 خوانده شده‌اند؛ زمانی که این صفحه دارای مهر «آخرین به‌روزرسانی: 2026-08-18 16:02 UTC» بود. این آمار تغییر می‌کنند. به جای نقل‌قول از این پاراگراف در شش ماه آینده، مستقیماً به صفحه ردیاب (tracker) مراجعه کنید.

یک نکته مهم‌تر از درصد وجود دارد. این چارچوب، هر بسته را دو بار روی سخت‌افزار اختصاصی خود با تغییر محیط بین دو بیلد می‌سازد و نتایج را با هم مقایسه می‌کند. این فرآیند اندازه‌گیری می‌کند که آیا یک بسته قابلیت ساخته‌شدن به صورت بازتولیدپذیر را دارد یا خیر. این یک بررسی برای اطمینان از تطابق .deb موجود در آرشیو با منبع نیست؛ تطبیق با آرتیفکت منتشرشده، وظیفهٔ جداگانه‌ای است که بر عهدهٔ rebuilderها قرار دارد. هر دو عدد مفید هستند. آن‌ها به پرسش‌های متفاوتی پاسخ می‌دهند، اما افراد اغلب عدد اول را به گونه‌ای نقل می‌کنند که گویی همان عدد دوم است.

اقدامات لازم روی سرور شخصی

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

  • زنجیره ابزار (toolchain) را ثابت کنید. یک base image که با تگ ارجاع داده شده باشد، ممکن است بدون اطلاع شما تغییر کند. به آن با استفاده از digest ارجاع دهید و digest را همراه با release ثبت کنید.
  • ورودی‌ها را ثبت کنید. فایل lockfile، مقدار digest ایمیج و نسخه کامپایلر را در کنار artifact نگه دارید. بیلدکردنی که محیط آن قابل بازسازی نباشد، قابل تکرار نیست و در نتیجه هرگز نمی‌توان آن را بررسی کرد.
  • در CI دو بار بیلد بگیرید و اگر خروجی‌ها متفاوت بودند، job را با خطا متوقف کنید. این کار هزینه یک بیلد اضافه را دارد اما nondeterminism را همان روزی که ایجاد می‌شود شناسایی می‌کند، نه یک سال بعد در زمان بروز حادثه.
  • مسیرهایی که کامپایلر در فایل تعبیه می‌کند را حذف کنید. برای Go، دستور go build -trimpath -buildvcs=false دایرکتوری بیلد و مهر زمانی کنترل نسخه را حذف می‌کند و go version -m ./app آنچه که واقعاً در فایل باینری قرار گرفته است را نمایش می‌دهد.
  • هشِ آنچه که مستقر (deploy) کرده‌اید را نگه دارید. زمانی که نیاز دارید بدانید آیا باینری در حال اجرا با یک نسخه از سورس‌کد مطابقت دارد یا خیر، این رکورد تنها چیزی است که می‌تواند پاسخگو باشد.

بررسی CI شامل چهار خط است:

set -eu
./build.sh && mv dist/app app.1
./build.sh && mv dist/app app.2
diffoscope --text - app.1 app.2

ابزار diffoscope در صورت تفاوت بین دو artifact با کد خروجی غیر صفر خارج می‌شود، بنابراین job به‌طور خودکار شکست می‌خورد و توضیحی خوانا در لاگ باقی می‌گذارد. این تمام ایده است که در مقیاس یک مخزن (repository) پیاده‌سازی شده: ادعای اینکه یک فایل باینری از یک سورس‌کد خاص تولید شده، باید چیزی باشد که ماشین دوم بتواند آن را تأیید کند.

FAQ

آیا build قابل‌تکرار (reproducible) به معنای امن بودن نرم‌افزار است؟

خیر. این ویژگی فقط ثابت می‌کند که فایل باینری با سورس‌کد مطابقت دارد و نه بیشتر. یک backdoor که در سورس‌کد عمومی commit شده باشد، به‌صورت قابل‌تکرار build می‌شود و هر کسی که آن را دوباره build کند، همان سورس مخرب را تأیید خواهد کرد. بسته‌ای که دارای یک CVE شناخته‌شده است، به‌طور کامل بازتولید می‌شود و همچنان آسیب‌پذیر باقی می‌ماند. قابلیت تکرارپذیری، یکی از موقعیت‌های حمله (یعنی ماشین build و مسیر تبدیل سورس به باینری) را حذف می‌کند. خواندن سورس‌کد و ردیابی آسیب‌پذیری‌ها وظایف جداگانه‌ای هستند که تکرارپذیری آن‌ها را برای شما انجام نمی‌دهد.

چرا با اینکه هیچ تغییری در سورس ایجاد نشده، دو build من با هم متفاوت هستند؟

تقریباً همیشه دلیل آن یک timestamp، یک مسیر (path) یا ترتیب فایل‌ها است. فرمت‌های آرشیو مانند tar و zip زمان آخرین تغییر فایل را ذخیره می‌کنند، بنابراین checkout کردن در ثانیه‌ای متفاوت، بایت‌های متفاوتی تولید می‌کند. اطلاعات دیباگ، دایرکتوری مطلق build را ثبت می‌کنند، بنابراین /home/alice/src و /build/pkg از کدهای یکسان، باینری‌های متفاوتی تولید می‌کنند. خواندن دایرکتوری‌ها، ورودی‌ها را بر اساس ترتیب فایل‌سیستم برمی‌گرداند، بنابراین فایل‌های object در خط لینک ممکن است در ماشین‌های مختلف ترتیب متفاوتی داشته باشند. دستور diffoscope build1 build2 را اجرا کنید تا گزارش به شما بگوید کدام‌یک از این موارد باعث تفاوت شده است، به‌جای اینکه حدس بزنید.

متغیر SOURCE_DATE_EPOCH چیست و آیا باید آن را تنظیم کنم؟

این یک متغیر محیطی استاندارد است که شامل یک عدد می‌شود: زمان آخرین تغییر سورس‌کد به ثانیه از تاریخ 1 ژانویه 1970 به وقت UTC. ابزارهایی که از این متغیر پشتیبانی می‌کنند، هر جا که قرار باشد ساعت سیستم را بخوانند، از این مقدار استفاده می‌کنند. آن را از طریق سیستم کنترل نسخه با export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) تنظیم کنید. این فرآیند خودکار نیست و یک راه‌حل کلی محسوب نمی‌شود. فقط ابزارهایی که آن را پیاده‌سازی کرده‌اند، این متغیر را می‌خوانند و اسکریپت‌های build شما باید خودشان آن را بخوانند؛ بنابراین اسکریپتی که date را فراخوانی می‌کند، تا زمانی که شما آن را تغییر ندهید، همچنان زمان فعلی را درج خواهد کرد.

وقتی یک rebuilder وضعیت BAD گزارش می‌دهد، چه باید کرد؟

پیش از انجام هر کار دیگری، گزارش را بخوانید. نتیجه BAD به این معناست که یک rebuild مستقل، بایت‌های یکسانی تولید نکرده است و دلیل معمول آن، غیرقطعی بودن (nondeterminism) در بسته‌بندی است، نه یک حمله. ابزار rebuilderd دقیقاً به همین دلیل می‌تواند یک گزارش diffoscope تولید کند. اگر تفاوت‌ها مربوط به timestampها، مسیرهای build یا ترتیب فایل‌ها باشد، این یک باگ در بسته‌بندی است که ارزش گزارش کردن دارد. اگر تفاوت در کد اجرایی است و چنین توضیحی برای آن وجود ندارد، استقرار آن build را متوقف کنید، آرتیفکت‌ها را نگه دارید و موضوع را به ناشر ارجاع دهید.

#reproducible-builds#supply-chain#diffoscope#debian#verification