مبانی معماری امنیت اندروید – بخش دوم
مقاله
  • ۳۰ بهمن ۱۴۰۲
  • Learning Road Map
  • ۱۲ دقیقه خواندن

مبانی معماری امنیت اندروید – بخش دوم

محمدرضا تیموری
برای مطالعه‌ی بخش اول این مقاله، به اینجا مراجعه کنید.

بخش ششم – کتابخانه‌های فریم‌ورک اندروید (Android Framework Libraries)

لایه‌ی بعدی استک ما در معماری اندروید، کتابخانه‌های فریم‌ورک اندروید است. فریم‌ورک اندروید شامل همه‌ی کتابخانه‌های جاوا به‌جز کتابخانه‌های runtime اندروید می‌شود که بخش مهمی از آن مختص پکیج‌های لایه بالای اندروید است. در این فریم‌ورک‌ اجزای مورد نیاز برای ساخت اپلیکیشن‌های اندرویدی تعبیه شده است. به‌عنوان مثال می‌توان به کلاس‌های پایه برای activityها، serviceها و content providerها اشاره کرد که با android.view.something یا android.widget شناخته می‌شوند. فریم‌ورک اندروید همچنین شامل کلاس‌هایی برای فایل‌ها و دسترسی به دیتابیس است که بیشتر آن‌ها با پکیج‌های android.database.something یا android.content.something وجود دارند. کلاس‌هایی نیز برای تعامل با سخت‌افزار وجود دارند. همه‌ی کارکرد (functionality) سیستم‌عامل اندروید روی سطح کرنل، به‌عنوان سرویس‌های سیستمی پیاده‌سازی شده است؛ اما به‌صورت مستقیم در فریم‌ورک قابل دسترسی نیست. در واقع این دسترسی از طریق برخی کلاس‌های خارجی به نام manager امکان‌پذیر است. به‌صورت معمول، هر manager توسط یک سرویس سیستمی پشتیبانی می‌شود. به‌عنوان مثال، Bluetooth Manager یک نمای خارجی برای Bluetooth Manager Service است.

بخش هفتم – اپلیکیشن‌ها (Applications)

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

اپلیکیشن‌های سیستمی

اپلیکیشن‌های سیستمی به‌صورت پیش‌‎فرض در image مربوط به سیستم‌عامل وجود دارند. این اپلیکیشن‌ها Read-Only هستند و به‌صورت معمول در دایرکتوری /system قابل مشاهده خواهند بود. همچنین این اپلیکیشن‌ها قابل حذف شدن نیستند و توسط کاربران امکان تغییر آن‌ها وجود ندارد. این اپلیکیشن‌ها ایمن فرض شده و به آن‌ها نسبت به اپلیکیشن‌های نصب‌شده توسط کاربر، دسترسی‌های بسیار بیشتری می‌دهند. اپلیکیشن‌های سیستمی می‌توانند عضو اصلی سیستم‌عامل اندروید باشند یا می‌توانند به‌عنوان یک اپلیکیشن ساده‌‌ی از‌پیش‌‌نصب‌شده باشند؛ مانند email clients یا browser. در‌حالی‌که همه‌ی اپلیکیشن‌ها در پوشه‌ی /system نصب می‌شوند، در اندروید‌های جدیدتر، اپلیکیشن‌های سیستمی در پوشه‌ی /system/priv-app/ قرار دارند که به این پوشه، سطح دسترسی بالاتری نیز داده می‌شود. اپلیکیشن‌هایی که توسط کلید مخصوص پلتفرم امضا می‌شوند، می‌توانند مجوزهای سیستمی را داشته باشند که در این مکانیزم به‌وسیله‌ی یک امضای مشخص محافظت خواهند شد. در این حالت، این اپلیکیشن‌ها حتی می‌توانند دسترسی‌های مخصوص سطح سیستم‌عامل را نیز دریافت کنند. از طرفی اپلیکیشن‌هایی نیز که به‌صورت پیش‌فرض نصب هستند و قابل حذف شدن یا تغییر دادن نیستند، می‌توانند به‌وسیله‌ی اجازه‌ای که کاربر به آن‌ها می‌دهد، update شوند. عملیات update زمانی انجام می‌شود که آن اپلیکیشن‌ها با یک کلید خصوصی مشترک امضا شده باشند تا اپلیکیشن‌های موجود در دستگاه بتوانند آن امضا را تأیید کنند. البته بعضی از اپلیکیشن‌ها قابلیت override دارند. به‌عنوان مثال، کاربر این امکان را دارد که application launcher جداگانه‌ای را از طریق third-party applications نصب کند.

اپلیکیشن‌های نصب‌شده توسط کاربر (user-installed apps)

تمام اپلیکیشن‌هایی که کاربر نصب می‌­کند، بر روی یک پارتیشن از نوع read-write است. این پارتیشن /data نام دارد. این پوشه حاوی اطلاعات کاربر است که در زمان uninstall اپلیکیشن، اطلاعات مرتبط پاک خواهد شد. هر اپلیکیشن در یک sandbox امنیتی اختصاصی اجرا می‌شود و باقی می‌ماند؛ به این معنی که به اطلاعات اپلیکیشن‌های دیگر دسترسی ندارد. بحث­‌هایی مانند Privilege Separation و Least Privilege نیز در این بخش مطرح هستند.

کامپوننت‌های مختلف اپلیکیشن‌های اندرویدی

یک اپلیکیشن اندرویدی ترکیبی از کامپوننت‌­های مختلف است و برخلاف اپلیکیشن‌­های سنتی، می‌­تواند بیش از یک Entry Point داشته باشند. هر کامپوننت می­‌تواند entry point دل‌خواه خود را ارائه دهد که توسط اعمالی که کاربر انجام می­‌دهد، اپلیکیشن‌­های دیگر یا توسط یک event سیستمی، فراخوانی خواهد شد. کامپوننت­‌ها و entry pointهای آن‌ها، مانند metadataهای سنتی، در فایل manifest آن اپلیکیشن تعریف شده‌­اند که به آن AndroidManifest.xml می­‌گویند. مانند همه‌ی فایل­‌های اصلی اندروید، استفاده از یک فایل کمکی که اینجا از نوع xml است، به کم‌حجم شدن فایل‌های APK کمک می‌­کند تا parse کردن آن نیز با سرعت بالاتری انجام شود. شاید مهم‌ترین بخشی که در فایل android manifest وجود دارد، package name است که سیستم‌عامل، اپلیکیشن را با این نام می­‌شناسد (همان com.google.email یعنی برعکس domain name). در واقع فایل android-manifest در زمان نصب اپلیکیشن، parse شده و packageها و componentها در سیستم‌عامل ثبت می‌­شوند. سیستم‌عامل اندروید این پیش­‌نیاز را برای developerها گذاشته که هر اپلیکیشن توسط developer مرتبط sign یا امضای اختصاصی داشته باشد. این رویکرد تضمین می‌کند تا آن اپلیکیشنی که نصب شده است، توسط اپلیکیشن دیگری replace نشود و کلید آن، حتی در‌صورت به‌روزرسانی، با کلیدی که در سرور ذخیره شده، تفاوتی نکند. به این موضوع در مقاله‌های آینده پرداخته خواهد شد. به‌طور کلی کامپوننت­‌های اپلیکیشن‌­های اندرویدی به شرح زیر است:

Activities

activity مانند یک صفحه، همان interface کاربر است. در حقیقت activityها بلوک­‌های اصلی ساخت GUI هستند. اپلیکیشن‌ها activityهای مختلفی دارند که هر‌کدام می‌توانند با بقیه متفاوت باشند و در یک قالب جدا به کاربر نشان داده شوند. نکته‌ی مهم در اینجا این است که activityها می‌توانند به‌صورت جداگانه start شوند. همچنین اگر دسترسی لازم را داشته باشیم، امکان دارد با اپلیکیشن‌های دیگری نیز start شوند.

Services

کامپوننتی است که در background نصب می­‌شود و user-interface ندارد. اغلب از سرویس‌ها برای کارهای طولانی‌مدت استفاده می‌شود. مانند پخش موزیک یا دانلود کردن یک فایل، بدون این‌که ui را مشغول کند. جذابیت این موضوع برای ما این است که سرویس‌ها می‌توانند یک remote interface را به‌وسیله‌ی AIDL و functionalityهایی نیز برای اپلیکیشن‌های دیگر داشته باشند. برخلاف سرویس‌های سیستمی که جزئی از سیستم‌عامل هستند و همیشه در وضعیت running قرار دارند، سرویس‌­های اپلیکیشن‌ها می‌توانند در وضعیت Start یا Stop باشند.

Content Providers

در واقع یک interface است که برای اشتراک‌گذاری اطلاعات اپلیکیشن‌هایی کاربرد دارد که به‌صورت عمومی در دیتابیس یا فایل‌ها ذخیره می‌­شوند. یک نکته‌ی مهم این است که به‌وسیله‌ی IPC می‌توان به content provider دسترسی داشت و اطلاعات اپلیکیشن را به‌وسیله‌ی اپلیکیشن‌های دیگر مشاهده کرد. بخش Content Providerها از کنترل دسترسی fine-grade استفاده می­‌کنند. در واقع از‌آنجایی‌که اجزای مختلف اطلاعات اپلیکیشن با هم متفاوت است، امکان اشتراک‌گذاری زیر‌مجموعه‌­ای از اطلاعاتی که ذخیره شده، وجود دارد.

Broadcast Receivers

کامپوننتی است که به eventهایی پاسخ می‌دهد که توسط سیستم‌عامل ایجاد یا Broadcast شده‌اند. به‌عنوان مثال اگر تغییرات سیستمی در اتصالات شبکه پیش آمده باشد یا به‌روزرسانی و تغییرات اپلیکیشن در background به پایان رسیده باشد، event مربوط به آن توسط سیستم‌عامل Broadcast خواهد شد.

مدل امنیتی اندروید

مانند بقیه‌ی اجزای سیستم‌عامل، مدل امنیتی اندروید از مزیت‌هایی بهره می‌برد که کرنل در اختیار آن می­‌گذارد. لینوکس یک سیستم‌عامل چند‌کاربره است که می­‌تواند منابع مربوط به یک کاربر را از دید کاربر دیگر مخفی یا محیط ایزوله‌­ای را برای فرایندها فراهم کند. اندروید از این مزیت استفاده می‌کند؛ اما باید به این نکته توجه داشت که مانند لینوکس سنتی (نسخه‌های Desktop یا Server) عمل نمی­‌کند. در لینوکس سنتی، به کاربر فیزیکی‌ای که می‌خواهد به سیستم‌عامل Login کند، مقداری به نام UID و یک سرویس سیستمی یا daemon که در background اجرا می­‌شود، اختصاص می­‌یابد. (به‌طور معمول هر daemon بر روی شبکه قابل دسترسی است و با محدود کردن یک daemon مشخص، می­‌توان از ضربه زدن به سیستم‌عامل جلوگیری کرد.) اندروید به‌صورت پیش‌فرض برای smartphoneها ساخته شده است. به همین علت برای موبایل‌ها که وسیله‌هایی شخصی هستند، دیگر به ثبت user دیگری در سیستم نیازی نیست و سیستم‌عامل فقط با یک کاربر فیزیکی در تماس خواهد بود؛ در نتیجه از UID برای مشخص کردن اپلیکیشن‌های مختلف استفاده می‌شود. این موضوع، پایه‌ای برای sandboxing در اپلیکیشن‌های اندرویدی است.

Application Sandboxing

اندروید به‌صورت خودکار یک مقدار UID یکتا و خاص را به هر اپلیکیشنی می­‌دهد که به آن app id می‌­گویند. زمانی که اپلیکیشن نصب می­‌شود، UID به آن اختصاص می‌یابد و اگر اپلیکیشن اجرا شود، UID نیز در آن به اجرا در‌می‌­آید. همچنین به هر اپلیکیشن، یک پوشه اختصاص داده خواهد شد که در آن پوشه، فقط اجازه‌ی read و write داده می‌شود؛ در نتیجه اپلیکیشن‌ها در دو سطح مختلف ایزوله یا به‌اصطلاح sandbox می­‌شوند. سطح اول در لایه‌ی فرایند، با هربار اجرا در یک process مشخص و سطح دوم در لایه‌‎‌ی فایل است. همچنین هر اپلیکیشن یک دایرکتوری مجزا دارد. این قابلیت‌ها که یک sandbox در سطح kernel ایجاد می­‌کند، روی همه‌ی اپلیکیشن‌ها اعمال می‌­شود و دیگر در شیوه‌ی اجرا شدن آن‌ها که می­‌توانند به‌صورت native یا vm باشند، فرقی ندارد. Daemonهای سیستم‌عامل و اپلیکیشن‌ها در UIDهای دسته‌بندی‌شده و ثابتی اجرا خواهند شد که از یکدیگر به‌خوبی تفکیک شده‌­اند. Daemonهای خیلی کمی نیز با دسترسی root اجرا می­‌شوند. سیستم‌عامل اندروید فایل / etc / shadows سنتی را ندارد؛ ولی UIDهای سیستمی در header فایل android_filesystem_config.h به‌صورت ثابت مشخص شده‌­اند. UIDهایی که برای سرویس‌­های سیستم‌عامل کاربردی هستند، از شماره‌ی ۱۰۰۰ شروع می‌شوند و خود این شماره نیز برای کاربر system است که privilegeهای مخصوص‌به‌خود را دارد؛ اما باز هم محدودیت‌هایی وجود دارد. به‌صورت خودکار شماره‌ی UID برای اپلیکیشن‌ها از ۱۰۰۰۰ شروع می­‌شود. مقداری به‌عنوان نام کاربری نیز در قالب app_XXX یا uY_aXXX ساخته می­‌شود که البته این قالب برای نسخه‌هایی از اندروید است که از چند کاربر پشتیبانی می­‌کنند. XXX نشان‌دهنده‌ی offset از aid_app و ِY نشان‌دهنده‌ی ID کاربر اندروید است. به‌عنوان مثال UID با عدد ۱۰۰۳۷ به‌صورت u0_a37 نیز قابل نوشتن است که به اپلیکیشنی به نام google email نسبت داده می­‌شود.

Permissions

به‌دلیل اینکه اپلیکیشن‌های اندرویدی در sandbox هستند، تنها می­‌توانند به فایل‌های خودشان یا فایل­‌هایی دسترسی داشته باشند که از قبل برای آن‌ها تعریف شده است. البته این محدودیت اپلیکیشن‌ها از دیدگاه توسعه‌دهندگان سیستم‌عامل اندروید، مناسب نیست. در اندروید علاوه‌بر این فایل­‌ها، می­‌توان دسترسی‌های جدیدی تعریف کرد که به کارایی بهتر اپلیکیشن‌ها منجر می‌­شود. به این حقوق دسترسی، permission گفته می­‌شود. کاربرد آن، کنترل دسترسی به Hardware Devices ،Internet Connectivity ،Data یا سرویس‌های سیستم‌عامل است. اپلیکیشن‌ها می­‌توانند دسترسی مورد نیاز خود را در فایل AndroidManifest.xml ثبت کنند. در مرحله‌ی نصب، کاربر دسترسی­‌های درخواست‌شده را بررسی می‌کند. تنها با یک‌بار اعطای دسترسی، نمی‌توان آن را revoke کرد و در نتیجه، دیگر به تأیید دوباره‌ی دسترسی نیازی وجود ندارد. البته در مواقع محدودی مانند دسترسی به private key یا user account، به تأیید دسترسی دوباره نیاز است. دسترسی­‌هایی وجود دارد که فقط به سه نوع اپلیکیشن داده می‌­شود. دسته‌ی اول اپلیکیشن­‌هایی که جزئی از سیستم‌عامل اندروید هستند. دسته‌ی دوم اپلیکیشن‌های preinstall هستند که به آن‌ها از قبل دسترسی داده می­‌شود و در نهایت دسته‌ی سوم نیز شامل اپلیکیشن‌هایی می‌شود که توسط کلید سیستم‌عامل sign شده‌اند و دسترسی‌های مرتبط به آن‌ها داده می­‌شود. اپلیکیشن‌های third-party می­‌توانند دسترسی­‌های دل‌خواه خود را داشته باشند و محدودیت­‌های مشابهی را تعریف کنند که به نام protection levels است. بدین وسیله دسترسی به سرویس­‌های اپلیکیشن و منابعی که اپلیکیشن از آن‌ها استفاده می­‌کند، محدود می‌­شود.

Code Signing and Platform Keys

تمام اپلیکیشن‌ها (حتی اپلیکیشن‌های system) باید توسط توسعه‌دهنده‌ی آن امضا شوند. رویکرد امضا بر‌اساس JAR است؛ زیرا فایل‌های با فرمت APK، یک extension از قالب java jar package هستد. کاربرد امضای هر APK در اندروید این است که سیستم‌عامل از بابت به‌روزرسانی‌ای که برای یک اپلیکیشن دریافت می­‌شود، اطمینان حاصل کند (که به آن same origin policy نیز گفته می­‌شود). این رویکرد با مقایسه‌ی امضای گواهی‌نامه‌ی اپلیکیشنی که در دستگاه نصب شده و امضای آن اپلیکیشنی که دانلود شده و نیازمند به‌روزرسانی است، فراهم می‌شود. اپلیکیشن‌های سیستمی نیز توسط کلیدهایی به نام platform key امضا می‌­شوند. وقتی که کامپوننت­‌های مختلفِ سیستم‌عامل با یک platform key امضا شده باشند، می­‌توانند منابعی را که در اختیار دارند، با یکدیگر در یک process به اشتراک بگذارند. این platform keyها توسط همان کسی که از آن نسخه‌ی اندروید مراقبت و نگهداری می­‌کند، تولید و کنترل خواهد شد. به‌عنوان مثال تولید‌کننده‌ها، carrierها، Google برای دستگاه‌های Nexus یا حتی خود کاربران برای نسخه‌های اندروید self-build.

SELinux

مدل امنیت سنتی اندروید، به UIDها و GIDهایی متکی است که مربوط به اپلیکیشن هستند. با اینکه این مدل توسط کرنل تضمین شده است (به‌صورت پیش‌فرض همه‌ی فایل‌های اطلاعات مربوط به اپلیکیشن خصوصی هستند)، هیچ مکانیزمی وجود ندارد که از اعطای دسترسی به فایل‌های یک اپلیکیشن جلوگیری کند؛ مانند وقوع یک خطای برنامه‌نویسی یا هر مورد دیگری. هیچ‌چیزی نمی‌تواند مانع از سوءاستفاد‌ه‌ی اپلیکیشن مخرب از permissionها و سوکت‌های محلی شود. در واقع اعطای permission به بعضی از اپلیکیشن‌ها یا system fileها، می‌­تواند منبع آسیب‌پذیری­‌های اندروید باشد. از این آسیب‌پذیری‌ها بر طبق مدل کنترل دسترسی لینوکس که به نام Discretionary Access Control یا DAC است، نمی‌توان جلوگیری کرد. DAC به این معنی است که زمانی که کاربر دسترسی‌هایی را به یک سری از منابع خاص دریافت می‌کند، این دسترسی­‌ها می‌توانند به‌وسیله‌ی کاربران دیگر نیز مورد استفاده قرار بگیرند. به‌عنوان مثال تنظیم یک فایل به‌صورتی که همه بتوانند آن را بخوانند (world-readable). در مقابل، مکانیزم MAC وجود دارد که باعث می­‌شود دسترسی به منابع در کل سیستم، از طریق یک سری authorization ruleها صورت پذیرد که به آن­‌ها policy نیز گفته می­‌شود. برای اینکه کاربرها نتوانند یک فایل را override یا آن را در دسترس کاربران دیگر قرار دهند، policy فقط زمانی تغییر می‌یابد که administrator آن تغییرات را اعمال نماید. بحث SELinux یا Security Enhanced Linux یک مکانیزم MAC است که برای کرنل لینوکس فراهم شده و بیشتر از ۱۰ سال است که استفاده می‌شود. در نسخه‌ی ۴.۳ اندروید، مکانیزمی شبیه به SELinux به نام SEAndroid تعبیه شده که به‌وسیله‌ی آن، قابلیت‌هایی که در اندروید وجود دارد (مثل Binder)، در مکانیزم SELinux گنجانده شود. در اندروید، SELinux برای ایزوله کردن daemonهای اصلی اندروید و اپلیکیشن‌ها استفاده می‌شود که به آن‌ها domainهای متفاوتی نسبت داده شده است. همچنین برای هر domain، یک policy متناظر تنظیم می‌شود. در نسخه‌ی ۴.۴ اندروید، SELinux در حالت enforcing mode پیاده‌سازی شده است که اجبار می­‌کند از policyهای سیستم برای تولید خطاهای زمان یا runtime استفاده شود؛ اما policy enforcement تنها روی daemonهای اصلی سیستم اعمال خواهد شد. به عبارت دیگر اپلیکیشن‌ها در حالت permissive mode اجرا می­‌شوند و log مربوط به آن نیز تولید می­‌شود. شایان ذکر است این فرایند باعث به وجود آمدن خطای runtime نمی­‌شود.

جمع‌بندی

در این دو مقاله، قصد داشتیم که خواننده با مباحث پایه‌ی امنیت در اندروید یا همان مبانی معماری امنیتی اندروید آشنا شود. همچنین اصطلاحاتی مانند Activity ،Receiver و Permission را که در دنیای امروز امنیت اپلیکیشن‌های اندرویدی نقش مهمی دارند، به زبان ساده شرح دادیم. البته مکانیزم‌هایی مانند IPC در سیستم‌عامل اندروید نیز بیان شد که در فرایند نفوذ به اپلیکیشن‌های اندرویدی نقشی ندارند و بیشتر در لایه‌های زیرین سیستم‌عامل اندروید مطرح هستند. با استفاده از مطالبی که در این دو مقاله شرح داده شد، می‌توانید اپلیکیشن‌های اندرویدی را از نظر امنیتی بررسی کنید. با توجه به قابلیت فوق‌العاده‌ی شخصی‌سازی در اندروید (که از کرنل لینوکسی آن نشئت گرفته شده)، توسعه‌دهندگان می‌توانند از مکانیزم‌های کنترلی و امنیتی متفاوتی به‌منظور جلوگیری از نفوذ هکرها به آن استفاده نمایند.

ویدیوی شرح مقاله توسط «محمدرضا تیموری»