系统安全机制——Android/Linux

对Android系统和Linux系统的基本安全机制进行总结。

Android安全机制

1.Android系统架构

下面是Google官方提供的Android系统经典分层架构图,从下往上依次分为Linux内核、HAL、系统Native库和Android运行时环境、Java框架层以及应用层这5层架构,其中每一层都包含大量的子模块或子系统。

Android系统架构开篇

2.Android安全机制

A.概述

Android安全模型延用了 Linux的安全机制,并且从进程、内存管理、进程间通信、权限等方面进一步增强,具体而言,使应用程序受保护于“沙箱”机制,采用更为安全、高效的进程间通信机制 Binder,提供 ashmem机制辅助有效地管理不再使用的内存,要求应用程序配置访问权限并采用数字签名。

Android安全模型主要提供以下几种安全机制:

  1. 进程沙箱隔离机制。 Android应用程序在安装时被赋予独特的用户标识(UID),并永久保持;应用程序及其运行的 Dalvik虚拟机运行于独立的 Linux进程空间,UID不同的应用程序完全隔离。
  2. 应用程序签名机制。应用程序包(.apk文件)必须被开发者数宇签名;同一开发者可指定不同的应用程序共享UID,进而运行于同一进程空间,共享资源。
  3. 权限声明机制。应用程序需要显式声明权限、名称、权限组与保护级别。不同的级别要求应用程序行使此权限时的认证方式不同: normal级申请即可用; Dangerous级需在安装时由用户确认才可用; Signature与 Signatureorsystem则必须是系统用户才可用。
  4. 访问控制机制。传统的Linux访问控制机制确保系统文件与用户数据不受非法访问。
  5. 进程通信机制。 Binder进程通信机制提供基于共享内存的高效进程通信; Binder基于 Client-Server模式,提供类似COM与 CORBA的轻量级远程进程调用(RPC);通过接口描述语言(AIDL)定义接口与交换数据的类型,确保进程间通信的数据不会溢出越界,污染进程空间。
  6. 内存管理机制。基于标准 Linux的低内存管理机制(OOM),设计实现了独特的低内存清理(LMK)机制,将进程按重要性分级、分组,当内存不足时,自动清理最低级别进程所占用的内存空间;同时,引入不同于传统Linux共享内存机制的Android共享内存机制 Ashmem,具备清理不再使用共享内存区域的能力。

B.Linux安全模型简介

Android系统以 Linux内核为基础,理解 Android的安全设计首先要厘清 Linux安全模型的主要概念与元素,包括用户与权限、进程与内存空间等。

(1)用户与权限

Linux安全模型的基础是用户与用户组,Linux的用户由用户名与用户标识(UID)表示。用户可同时参与多个用户组,每个用户组由组标识(GID)表示。

(2)进程与内存空间

Linux是一个多用户与多进程的操作系统,允许多个用户同时运行各自隔离的多个应用进程,并且通过用户及权限对用户资源进行隔离与保护。Linux的用户、进程、内核、设备之间的关系如图所示,可总结为——

  1. Linux内核允许多个用户同时存在并运行不同的进程。
  2. 每个用户拥有多个同时运行的进程,多个进程分别属于不同的用户。
  3. 所有进程(无论是否属于同一用户)各自运行于独立的内存空间。
  4. 用户进程通过系统调用接口访问操作系统的服务。
  5. 操作系统内核通过设备驱动访问硬件设备与资源,如数据存储与网络设备等。

C.Android安全机制补充介绍

进程沙箱

每个APP在各自独立的Dalvik虚拟机中运行,拥有独立的地址空间和资源。运行于Dalvik虚拟机中的进程必须依托内核层Linux进程而存在,因此Android使用Dalvik虚拟机和Linux的文件访问控制来实现沙箱机制。

  1. Windows与UNIX/Linux等传统操作系统以用户为中心,假设用户之间是不可信的,更多考虑如何隔离不同用户对资源(存储区域与用户文件、内存区域与用户进程、底层设备等)的访问(即访问控制)。

  2. 不同于访问控制,针对的是系统内文件(Linux系统中,设备资源也是文件);沙箱隔离概念,是针对运行中的进程。

  3. Android扩展了Linux内核安全模型的用户与权限机制,将多用户操作系统的用户隔离机制巧妙地移植为应用程序隔离。在Linux中,一个用户标识(UID)识别一个给定用户;在 Android上,一个UID则识别一个应用程序。

  4. 在安装应用程序时向其分配UID。应用程序在设备上存续期间内,其UID保持不变。权限用于允许或限制应用程序(而非用户)对设备资源的访问。

  5. 在很多情况下,源自同一开发者或同一开发机构的应用程序,相互间存在信任关系,Android系统提供一种所谓共享UID( SharedUserlD)机制,使具备信任关系的应用程序可以运行于同一进程空间。通常,这种信任关系由应用程序的数字签名确定,并且需要应用程序在 manifest文件中使用相同的UID。

  6. 进程沙箱为互不信任的应用程序之间提供了隔离机制, SharedUserID则为具备信任关系的应用程序提供了共享资源的机制。

应用权限

权限机制是 Android安全机制的基础,决定允许还是限制应用程序访问受限的API和系统资源。
由于用户自行安装的应用程序也不具备可信性,在默认情况下, Android应用程序没有任何权限,不能访问保护的设备API与资源。
应用程序的权限需要明确定义,在安装时被用户确认,并且在运行时检查、执行、授予和撤销权限。
在定制权限下,文件和内容提供者也可以受到保护。

Android根据不同的用户和组,分配不同权限,比如访问网络、访问GPS数据等,这些 Android权限在底层映射为 Linux的用户与组权限

进程通信

对于IPC(Inter-Process Communication, 进程间通信),Linux现有管道、消息队列、共享内存、套接字、信号量、信号这些IPC机制,Android额外还有Binder IPC机制。

对于Android上层架构,则最常用的通信方式是Binder、Socket、Handler,当然也有少量其他的IPC方式,比如杀进程Process.killProcess()采用的是signal方式。

A.Binder

基于共享内存的Binder实现,提供轻量级的远程进程调用(RPC)。通过接口描述语言(AIDL)定义接口与交换数据的类型,确保进程间通信的数据不会溢出越界。

在系统安全设计方面,Android的Binder进程通信机制设计具备优于传统 Linux的重要优势。

  • Android应用基于权限机制,定义进程通信的权限,相比传统 Linux IPC具有更细粒度的权限控制
  • Binder进程间通信机制具备类型安全的优势。开发者在编译应用程序时,使用Android接口描述语言(AIDL)定义交换数据的类型,确保进程间通信的数据不会溢出越界污染进程空间。
  • Binder通过 Android的共享内存机制(Ashmem)实现高效率的进程通信,而不是采用传统的 Linux/UNIX共享内存( Shared Memory),也具备特殊的安全含义。

AIDL的接口定义与参数描述是类型安全的。

Android应用程序使用Java语言编写。Java语言就具备所谓的“类型安全”特性,是一种强类型化的编程语言,它强制不同内容遵循规定的数据格式,进而防止错误或恶意应用。
不完整的类型安全与边界检查机制极易受到内存污染或缓冲区溢出攻击,进而导致任意代码,甚至恶意代码的运行
但是,在C/C++程序设计中,允许未经类型检查的强制类型转换,而且,除非编程者专门编程进行边界检查,否则C语言本身不要求边界检查。实践证明,这些C/C++语言的灵活性恰恰成为恶意代码攻击的目标。
Android系统原生库允许采用C/C++编程,存在一定的安全隐患,需要其他特殊技术加以防范。
传统 Linux的进程通信机制虽然有用户权限的限制,但缺少强制的类型安全。

B.Socket

Socket通信方式也是C/S架构,比Binder简单很多。在Android系统中采用Socket通信方式的主要有:

  • zygote:用于孵化进程,system_server创建进程是通过socket向zygote进程发起请求;
  • installd:用于安装App的守护进程;
  • lmkd:lowmemorykiller的守护进程,Java层的LowMemoryKiller最终都是由lmkd来完成;
  • adbd:用于服务adb;
  • logcatd:用于服务logcat;
  • vold:即volume Daemon,是存储类的守护进程,用于负责如USB、Sdcard等存储设备的事件处理。

Socket方式更多的用于Android framework层与native层之间的通信。

C.Handler

Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信。 Handler消息机制是由一组MessageQueue、Message、Looper、Handler共同组成的,为了方便且称之为Handler消息机制。

内存管理

A.Ashmem匿名共享内存

android的匿名共享内存(Ashmem)机制基于 Linux内核的共享内存,但是 Ashmem与 cache shrinker关联起来,增加了内存回收算法的注册接口,因此 Linux内存管理系统将不再使用的内存区域加以回收。

B.LMK(LowMemoryKiller)机制

Android内存管理机制基于Linux的OOM(out of memory killer)机制,实现了LMK(low memory killer)机制。将所有的进程按照重要性进行分级,系统会自动清理最低级别的进程所占用的内存空间。

每个程序都有一个oom_adj值,值越小,优先级越高,被杀死的可能性越低。Android将程序的重要性分为几类。每一类的内存警戒值与oom_adj值如下图所示——

D.Android安全机制变迁

在 Android系统中虽然大量的应用程序由类型安全的Java程序设计实现,但底层软件中仍大量使用C/C++代码,受到安全攻击的可能性时刻存在。

Android系统的SDK、编译器及操作系统工具为防范内存溢出攻击提供了相应的辅助手段,利用现代移动处理器(CPU)的虚拟内存管理(MMU)的安全特性等对内存管理的安全性进行了增强。在 Android版本升级过程中,自1.5版本之后,引人了许多安全机制,如IBM的 ProPolice检测堆栈溢出, OpenBSD的 dlmalloc等。

下图是对一些典型的安全机制的简介——

下图是大佬整理的Android安全机制随版本更新变迁脑图,主要为漏洞缓解和一些安全措施。

E.参考

《Android安全机制解析与应用实践》
理解Android安全机制
Android 安全机制
Android安全机制
Android系统架构开篇
Android(十二)安全机制变迁脑图
Android Q:安全与隐私

Linux安全机制

由于时间有限,这里只是对整体的Linux安全进行了简单的概述,其中的各项安全机制及具体作用(要想理解透彻,离不开对源码的阅读),等到需要的时候再进行详细查阅和理解。

1.概述

Linux系统安全包括Linux内核层的安全和用户层的安全。

用户层的安全包括Linux下的各种认证系统,比如目前流行的PAM认证机制,Ukey指纹认证机制,远程网络认证机制,LDAP认证机制,3A认证机制等。用户层安全还包括网络安全,比如通过iptables定制防火墙,关闭服务器不必要开启的端口等。

内核层的安全就包括了Linux操作系统的各种安全机制。现有的Linux安全框架叫做LSM(Linux security module)。基于这个框架,可以实现各种各样的Linux安全机制,其中最著名的就是SELinux

2.Linux内核安全

  1. 在安全性方面,Linux内核只提供了经典的UNIX自主访问控制(root用户,用户ID,模式位安全机制),以及部分的支持了POSIX.1e标准草案中的capabilities安全机制。

  2. 有很多安全访问控制模型和框架已经被研究和开发出来,用以增强Linux系统的安全性,比较知名的 SELinux,DTE,以及 LIDS 等等。但是由于没有一个系统能够获得统治性的地位而进入Linux内核成为标准;并且这些系统都大多以各种不同的内核补丁的形式提供,使用这些系统需要有编译和定制内核的能力,对于没有内核开发经验的普通用户,获得并使用这些系统是有难度的。即Linux内核需要有一个通用的安全访问控制框架。

  3. LSM是Linux内核的一个轻量级通用访问控制框架。它使得各种不同的安全访问控制模型能够以Linux可加载内核模块的形式实现出来,用户可以根据其需求选择适合的安全模块加载到Linux内核中,从而大大提高了Linux安全访问控制机制的灵活性和易用性。目前已经有很多著名的增强访问控制系统移植到LSM上实现,包括POSIX.1e capabilities、SELinux、DTE,以及 LIDS 等等。即LSM只是一个框架,用户可根据实际需要动态加载各种具体的安全增强功能模块。

  1. LSM本身不提供任何具体的安全策略,而是提供了一个通用的基础体系给安全模块,由安全模块来实现具体的安全策略。

LSM对于普通用户的价值就在于:可以提供各种安全模块,由用户选择适合自己需要的加载到内核,满足特定的安全功能。LSM本身只提供增强访问控制策略的机制,而由各个安全模块实现具体特定的安全策略。下面简要介绍一些已经实现的安全模块——

  • SELinux。这是一个Flask灵活访问控制体系在Linux上的实现,并且提供了类型增强,基于角色的访问控制,以及可选的多级安全策略。SELinux原来是作为一个内核补丁实现的,现在已经使用LSM重新实现为一个安全模块。SELinux可以被用来限制进程为最小特权,保护进程和数据的完整性和机密性,并且支持应用安全需求。
  • DTE Linux。这是一个域和类型增强在Linux上的实现。就像SELinux一样,DTE Linux原来是作为一个内核补丁实现的,现在已经使用LSM重新实现为一个安全模块。当这个安全模块被加载到内核上时,类型被赋给对象,域被赋给进程。DTE策略限制域之间和从域到类型的访问。
  • Openwall 内核补丁的LSM移植。Openwall内核补丁提供了一系列的安全特性集合来保护系统免受例如缓冲区溢出和临时文件竞争这样的攻击。
  • POSIX.1e capabilities。 Linux内核中已经存在有POSIX.1e capabilities逻辑,但是LSM把这个逻辑划分到了一个安全模块中。这样的修改使得不需要的用户可以从他们的内核中把这个功能略去;也使得capabilities逻辑的开发可以脱离内核开发获得更大的独立性。
  • LIDS。这是由国人谢华刚发起的项目。开始时作为一个入侵检测系统开发,后来逐渐演变为使用访问控制系统的形式来进行入侵预防,它通过描述一个给定的程序可以访问哪些文件来进行访问控制。同样的,LIDS原来是作为一个内核补丁实现的并附带了一些管理工具,现在已经使用LSM重新实现为一个安全模块。

当然还有缺省的传统超级用户机制。这个安全模块是LSM默认的,实现了传统的UNIX超级用户特权机制。

3.Linux Kernel Defence Map

Between the Millstones:Lessons of Self-Funded Participation in Kernel Self Protection Project

对于此图的学习和理解有待后续有时间和需要的时候进行。

4.参考

Linux系统安全
LSM学习
Linux操作系统中的安全机制—能力(capability))