پرونده ۰۴ — SKRED Messenger

یک کلید برای
همهٔ دستگاه‌ها.

SKRED پیام‌رسانی متن‌باز با رمزنگاری سرتاسری است. هنگام بررسی کد مدیریت کلید آن، کلید جایگزینی پیدا کردم که در همهٔ دستگاه‌ها یکسان بود. موضوع را از طریق BSI CERT-Bund گزارش کردم. سازنده نیز همان‌طور که امید می‌رود پاسخ داد: شفاف، با توضیح فنی دقیق و با اصلاحی سریع.

نویسنده: عباس حسینی· با هماهنگی BSI CERT-Bund و CERT/CC· سازنده: SKRED SAS / Twinlife

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

یک کلید مشترک برای چند گوشی یکسان همان کلید در همهٔ دستگاه‌ها
شکل ۱ — یک کلید برای همه

SKRED تنظیمات حساس را به‌صورت رمزگذاری‌شده روی دستگاه نگه می‌دارد. در حالت عادی، برای هر دستگاه کلیدی جداگانه در محل امن نگهداری کلید تلفن (Android Keystore) می‌سازد. اگر این کار شکست می‌خورد ــ مشکلی شناخته‌شده در بعضی دستگاه‌های قدیمی ــ یک کلید جایگزینِ ثابت وارد عمل می‌شد. این کلید در همهٔ نصب‌ها یکسان بود.

چرا چنین مسیری وجود داشت؟ هدف درست بود: حتی وقتی Keystore دستگاه از کار می‌افتد، داده‌ها همچنان محافظت شوند. اما کلید جایگزین مخصوص هر دستگاه نبود. از دو مقدار ثابت در کد به‌صورت قطعی ساخته می‌شد؛ بنابراین در همه‌جا یکی بود و می‌شد آن را از کد متن‌باز بازسازی کرد.

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

کلید جایگزین کنار گذاشته شده و فقط برای خواندن داده‌های قدیمی به کار می‌رود اکنون: توقف امن هنگام خطا کلید جایگزین فقط برای خواندن داده‌های قدیمی
شکل ۲ — اصلاح سازنده

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

پاسخ سازنده

پاسخی انسانی، اصلاحی واقعی.

پس از آن‌که BSI گزارش را منتقل کرد، مسئول فنی شرکت شخصاً پاسخ داد؛ نه با متنی آماده، بلکه بندبه‌بند و با توضیح فنی. مستندات بیشتری دربارهٔ معماری برنامه فرستاد، به کد متن‌باز ارجاع داد و اصلاح‌ها را بی‌درنگ آغاز کرد. در کنار مشکل کلید جایگزین، پین‌کردن گواهی نیز اضافه شد؛ همهٔ این تغییرها در مخزن عمومی کد قابل پیگیری‌اند.

آنچه از این پرونده می‌ماند

افشای هماهنگ باید این‌گونه باشد.

ارزش افشای هماهنگ به بزرگی یک حفرهٔ امنیتی نیست؛ به کاری است که پس از گزارش انجام می‌شود. SKRED نمونهٔ خوبی ارائه کرد: کد باز، پاسخ‌گویی یک انسان، همکاری به‌جای موضع‌گیری دفاعی، و اصلاح‌هایی سریع و قابل بررسی. متن‌باز بودن برنامه‌ها نیز بخشی از همین رویکرد است.

گزارش فنی

SKRED Messenger — بررسی و اصلاح

خلاصه

SKRED Messenger (محصول SKRED SAS / Twinlife، فرانسه) برای Android و iOS متن‌باز است (AGPL). نسخهٔ منتشرشدهٔ برنامه را همراه با کد متن‌باز بررسی کردم. این گزارش به دو مورد اصلاح‌شده می‌پردازد: کلید جایگزین ثابت AES در بخش مدیریت کلید، و نبود پیکربندی پین‌کردن گواهی. هر دو از مسیر افشای هماهنگ با مدیریت BSI CERT-Bund (و CERT/CC، شناسهٔ VU#629507) گزارش و به دست سازنده اصلاح شدند. وضعیت اصلاح‌ها در کد متن‌باز قابل بررسی است.

یافتهٔ ۱ · مدیریت کلید

کلید جایگزین قطعی AES، یکسان در همهٔ دستگاه‌ها

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

بخش مدیریت کلید (KeyChain) تنظیمات «ایمن‌شده» ذخیره‌شده روی دستگاه را با کلیدی از Android Keystore محافظت می‌کند. اگر ساخت این کلید شکست می‌خورد ــ مشکلی شناخته‌شده در بعضی دستگاه‌ها یا نسخه‌های قدیمی سیستم ــ برنامه در گذشته به یک کلید ثابت درون کد روی می‌آورد. این کلید با عملگر XOR از دو آرایهٔ بایتِ ثابت در کد به‌صورت قطعی ساخته می‌شد؛ بنابراین در همهٔ نصب‌ها یکسان بود:

// simplified from the open-source KeyChain implementation
static Key getDefaultSecretKey() {
    int n = Math.min(UUID1.length, UUID2.length);
    byte[] key = new byte[n];
    for (int i = 0; i < n; i++)
        key[i] = (byte) (UUID1[i] ^ UUID2[n - 1 - i]);
    return new SecretKeySpec(key, "AES");   // 128-bit, same on every device
}

اثر امنیتی

در دستگاهی که Keystore آن از کار افتاده بود، تنظیمات «ایمن‌شده» محلی با کلیدی عمومی و شناخته‌شده محافظت می‌شد. هر کس دسترسی محلی به چنین دستگاهی داشت، می‌توانست آن داده‌ها را رمزگشایی کند. این مسیر جایگزین بود، نه حالت معمول؛ دسترسی نیز محلی و وابسته به دستگاه بود. حمله‌ای از راه دور یا افشای محتوای پیام‌های همهٔ کاربران در میان نبود.

اصلاح سازنده

کلید جایگزین دیگر برای رمزگذاری استفاده نمی‌شود. اگر ساخت کلید در Keystore شکست بخورد، برنامه به‌جای رمزگذاری با کلید شناخته‌شده، با خطا متوقف می‌شود. این کلید اکنون فقط هنگام انتقال داده‌ها برای رمزگشایی داده‌های موجود به کار می‌رود. پشتیبانی از Android 5.0/5.1 ــ منشأ اصلی مشکل Keystore ــ نیز حذف شده است. این تغییرها در کد متن‌باز تأیید شدند.

یافتهٔ ۲ · امنیت شبکه

نبود پین‌کردن گواهی

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

برنامه بدون پیکربندی امنیت شبکه عرضه شده بود. وجود یک گواهی نصب‌شده روی دستگاه، مثلاً در محیط‌های مدیریت‌شده، ممکن بود امکان رهگیری ارتباطات TLS برنامه را فراهم کند.

اصلاح سازنده

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

دربارهٔ امتیاز شدت

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

هماهنگی و انتشار


افشای هماهنگ از مسیر BSI CERT-Bund و CERT/CC. اصلاح‌های سازنده در کد متن‌باز قابل پیگیری است. برای پژوهش و دفاع.