در ۱۹ و ۲۰ اکتبر ۲۰۲۶ تیم Go نسخه‌های Go 1.27.3 و 1.26.10 را منتشر کرد. این نسخه‌ها فقط یک اصلاح امنیتی دارند: CVE-2026-111943 (مسئله‌ی ۸۲۳۴۵ در Go). از این پس crypto/x509 زنجیره‌هایی را که شامل مجموعه‌ی کوچکی از گواهی‌های میانی CA به نام eMudhra (emSign) باشند رد می‌کند، چون کلید خصوصی آن‌ها لو رفته است. اعلامیه نمی‌گوید کدام گواهی‌ها، برای همین خودم سراغش رفتم.

اگر باینری Go تحویل می‌دهید، آپدیت کردن Go روی سیستم خودتان کافی نیست. هر باینری باید با toolchain اصلاح‌شده دوباره build شود.

خلاصه

شناسهCVE-2026-111943
بخشcrypto/x509 (تابع Certificate.Verify)
نوع ضعفCWE-295، اعتبارسنجی نادرست گواهی
نسخه‌های اصلاح‌شدهGo 1.26.10 و 1.27.3
شدتمنابع هم‌نظر نیستند: ۶٫۸ (متوسط) از Red Hat، ۸٫۱ (بالا) در ارزیابی اولیه‌ی Oracle

چه اتفاقی افتاد؟

طبق گزارش‌هایی که پیدا کردم، یک پژوهشگر در ۱۷ اکتبر متوجه شد هشت گواهی میانی زیرمجموعه‌ی emSign شرکت eMudhra با فرایند تولید کلید معیوب ساخته شده‌اند. Mozilla بعداً نتیجه گرفت این نقص ساده قابل سوءاستفاده است. این گواهی‌ها CAهای DV، EV، OV (برای TLS) و OV برای امضای کد هستند، هر کدام در نسل‌های G3B و G3C.

هر کس بتواند کلید خصوصی یکی از این‌ها را بازسازی کند، می‌تواند برای هر دامنه‌ای گواهی بسازد و کلاینتی که به این زنجیره اعتماد دارد آن را می‌پذیرد.

اصلاح Go چه می‌کند؟

حالا Verify هر زنجیره‌ای را که شامل یکی از میانی‌های مسدودشده باشد حذف می‌کند و به فراخواننده برنمی‌گرداند. فیلتر یک فهرست ثابت از هشت هش SHA-256 است که روی SubjectPublicKeyInfo (SPKI) هر گواهی حساب می‌شود، نه روی کل گواهی.

انتخاب درستی است. یک کلید ممکن است در چند گواهی بیاید، مثلاً یک میانی که از دو ریشه cross-sign شده. هش کل گواهی در هر کدام فرق دارد ولی هش SPKI یکی است، پس یک ورودی همه‌ی آن‌ها را پوشش می‌دهد.

آیا فهرست کامل است؟

یک denylist سخت‌کد‌شده فقط به اندازه‌ی کامل بودنش به درد می‌خورد. برای همین در ۲۶ اکتبر ۲۰۲۶ آن را با بسته‌ی مدرک پژوهشگر (proof.zip) مقایسه کردم:

۱. اول خود بسته را چک کردم. openssl dgst -sha384 -verify <name>.pub.pem -signature <name>.sig message.txt برای هر هشت مورد Verified OK داد و هر کلید عمومی با کلید داخل گواهی‌اش یکی بود. ۲. SHA-256 مربوط به DER بخش SubjectPublicKeyInfo هر .crt.pem را حساب کردم، یعنی همان ورودی‌ای که containsDistrustedSPKIHash استفاده می‌کند. ۳. distrustedSPKIHashes را از src/crypto/x509/verify.go در تگ go1.27.3 برداشتم.

نتیجه تطابق دقیق است: هشت از هشت، و هیچ هش اضافه‌ای در فهرست Go نیست.

گواهیSHA-256 مربوط به SPKI
EM DV TLS CA - G3Bebed6475c2c2d80fcb199f3fe8dd6e6b3ce60b77eb15d4e42c3dec0c6f5b5105
EM DV TLS CA - G3C49dd353dc2b919a3770645117d0e60c16115e7d1bc259eefe734b00ea434fe5f
EM EV TLS CA - G3Be0254c7fcdc265e2f0f8a428dabb75ab4ae039b4872d62f5ee57f0877d5a8a41
EM EV TLS CA - G3C5c0660a2b164910d8ff589c18e14552e9e0ec237dcefe2cb736d41a4811f81a0
EM OV TLS CA - G3B43bffa19d6ef651451f6841e019931e96f6f2eaeff28ab4b6f1ce4576de2717b
EM OV TLS CA - G3C63ecefee431d49523bc1136c4e72d0206a477086caf606cd5cf025686c109cf8
EM OV CS CA - G3Bec4ffa1ad0ac88081314b7aaffef6a91f97cd57291bb5ba686c5415f5eb4f6b6
EM OV CS CA - G3C92c063d67e8f14db71d288e11266ffdaf69cbf115f7bab14f3bc2f68d25e0e30

در سورس 1.27.3 فیلتر روی همه‌ی مسیرها اجرا می‌شود: verify.go برای verifier خالص Go و root_darwin.go و root_windows.go برای verifierهای پلتفرم. گواهی جعلی از هر یک از این هشت، به هر ریشه‌ای هم وصل شود رد می‌شود.

این تطابق نسبت به بسته‌ی پژوهشگر است که DV، EV، OV-TLS و OV-CS را در G3B و G3C پوشش می‌دهد. اگر فرایند معیوب یک میانی نهم هم ساخته باشد که کسی گزارش نکرده، این فهرست آن را نمی‌گیرد و فقط خود eMudhra یا برنامه‌های ریشه می‌توانند آن را رد کنند. یک زنجیره‌ی جعلی را هم روی باینری واقعی Go آزمایش نکردم.

شکاف عملیاتی

مرورگرها این مشکل را با ابطال از راه دور (CRLSet، OneCRL و CRLite) حل کردند. Go چنین کانالی ندارد، چون فهرست داخل سورس کتابخانه‌ی استاندارد است. پس:

  • باینری‌ای که قبل از اصلاح build شده، برای همیشه به هر هشت میانی اعتماد می‌کند. هیچ داده‌ی ابطالی به آن نمی‌رسد.
  • crypto/x509 در Go شفافیت گواهی (Certificate Transparency) را اعمال نمی‌کند، پس گواهی جعلی با CT هم لو نمی‌رود.
  • نصب Go جدید روی باینری‌های موجود اثری ندارد. توزیع‌ها هم ممکن است اصلاح را با شماره‌ی نسخه‌ی خودشان backport کنند.

چه کار کنیم؟

۱. ببینید هر باینری با چه toolchain ای build شده:

go version -m ./your-binary | head -1

هر چه با Go قدیمی‌تر از 1.26.10 (در شاخه‌ی 1.26) یا 1.27.3 (در شاخه‌ی 1.27) ساخته شده باید دوباره build شود، و ایمیج‌های کانتینری ساخته‌شده از آن‌ها هم.

۲. دوباره build و deploy کنید و نسخه‌ی Go را در CI ثابت کنید.

۳. اعتبارسنجی گواهی را خاموش نکنید (InsecureSkipVerify) تا مشکلی مربوط به این تغییر را دور بزنید.

۴. اگر می‌خواهید مطمئن شوید، فهرست را خودتان ببینید. در toolchain اصلاح‌شده:

grep -A12 distrustedSPKIHashes "$(go env GOROOT)/src/crypto/x509/verify.go"

و برای هش گواهی‌ای که دارید:

openssl x509 -in cert.pem -noout -pubkey \
  | openssl pkey -pubin -outform DER \
  | openssl dgst -sha256

منابع