برای مطالعهی بخش اول این مقاله، به
اینجا مراجعه کنید.
بخش ششم – کتابخانههای فریمورک اندروید (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 در سیستمعامل اندروید نیز بیان شد که در فرایند نفوذ به اپلیکیشنهای اندرویدی نقشی ندارند و بیشتر در لایههای زیرین سیستمعامل اندروید مطرح هستند.
با استفاده از مطالبی که در این دو مقاله شرح داده شد، میتوانید اپلیکیشنهای اندرویدی را از نظر امنیتی بررسی کنید. با توجه به قابلیت فوقالعادهی شخصیسازی در اندروید (که از کرنل لینوکسی آن نشئت گرفته شده)، توسعهدهندگان میتوانند از مکانیزمهای کنترلی و امنیتی متفاوتی بهمنظور جلوگیری از نفوذ هکرها به آن استفاده نمایند.
ویدیوی شرح مقاله توسط «محمدرضا تیموری»