در ۱۹ و ۲۰ اکتبر ۲۰۲۶ تیم 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 - G3B | ebed6475c2c2d80fcb199f3fe8dd6e6b3ce60b77eb15d4e42c3dec0c6f5b5105 |
| EM DV TLS CA - G3C | 49dd353dc2b919a3770645117d0e60c16115e7d1bc259eefe734b00ea434fe5f |
| EM EV TLS CA - G3B | e0254c7fcdc265e2f0f8a428dabb75ab4ae039b4872d62f5ee57f0877d5a8a41 |
| EM EV TLS CA - G3C | 5c0660a2b164910d8ff589c18e14552e9e0ec237dcefe2cb736d41a4811f81a0 |
| EM OV TLS CA - G3B | 43bffa19d6ef651451f6841e019931e96f6f2eaeff28ab4b6f1ce4576de2717b |
| EM OV TLS CA - G3C | 63ecefee431d49523bc1136c4e72d0206a477086caf606cd5cf025686c109cf8 |
| EM OV CS CA - G3B | ec4ffa1ad0ac88081314b7aaffef6a91f97cd57291bb5ba686c5415f5eb4f6b6 |
| EM OV CS CA - G3C | 92c063d67e8f14db71d288e11266ffdaf69cbf115f7bab14f3bc2f68d25e0e30 |
در سورس 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