Android安全模型之Android安全机制(进程通信)
进程通信是应用程序进程之间通过操作系统交换数据与服务对象的机制。Linux操作系统的传统进程间通信(IPC)有多种方式,比如管道,命令管道,信号量,共享内存,消息队列,以及网络与Unix套接字等。虽然理论上Android系统仍然可以使用传统的Linux进程通信机制,但是在实际中,Android的应用程序几乎不使用这些传统方式。在Android的应用程序设计架构下,甚至看不到进程的概念,取而代之的是从组件的角度,如Intent,Activity,Service,Content Provider,实现组件之间的相互通信。Android应用程序通常由一系列Activity和Service组成的,一般Service运行在独立的进程中, Activity既可能运行在同一个进程中,也可能运行在不同的进程中。在不同进程中的Activity和Service要协作工作,实现完整的应用功能,必须进行通信,以获取数据与服务。这就回归到历史久远的Client-Server模式。基于Client-Server的计算模式广泛应用于分布式计算的各个领域,如互联网,数据库访问等。在嵌入式智能手持设备中,为了以统一模式向应用开发者提供功能,这种Client-Server方式无处不在。Android系统中的媒体播放,音视频设备,传感器设备(加速度,方位,温度,光亮度等)由不同的服务端(Server)负责管理,使用服务的应用程序只要作为客户端(Client)向服务端(Server)发起请求即可。
但是,Client-Server方式对进程间通信机制在效率与安全性方面都是挑战。
效率问题。传统的管道,命名管道,网络与UNIX套接字,消息队列等需要多次复制数据(数据先从发送进程的用户区缓存复制到内核区缓存中,然后再从内核缓存复制到接收进程的用户区缓存中,单向传输至少有两次复制),系统开销大。传统的共享内存(shmem)机制无需将数据从用户空间到内核空间反复复制,属于低层机制,但应用程序直接控制十分复杂,因而难以使用。
安全问题。传统进程通信机制缺乏足够的安全措施:首先,传统进程通信的接收进程无法获得发送进程可靠的用户标识/进程标识(UID/PID),因而无法鉴别对方身份。Android的应用程序有自己UID,可用于鉴别进程身份。在传统进程通信中,只能由发送进程在请求中自行填入UID与PID,容易被恶意程序利用,是不可靠的。只有内置在进程通信机制内的可靠的进程身份标记才能提供必要的安全保障。其次,传统进程通信的访问接入点是公开的,如FIFO与unix domain socket的路径名,socket的ip地址与端口号,lSystem V键值等,知道这些接入点的任何程序都可能试图建立连接,很难阻止恶意程序获得连接,如通过猜测地址获得连接等。
Android基于Dianne Hackborm的OpenBinder实现,引入Binder机制以满足系统进程通信对性能效率和安全性的要求。Binder基于Client-Server通信模式,数据对象只需一次复制,并且自动传输发送进程的UID/PID信息,同时支持实名Binder与匿名Binder。Binder其实提供了远程过程调用(RPC)功能,概念上类似于COM和CORBA分布式组件架构。对于熟悉Linux环境的程序设计者而言,从Linux意义的进程通信角度来看,Android的进程通信原理如下图所示:
Binder进程通信机制由一系列组件组成:Client,Server,Service Manager,以及Binder Driver。其中,Client,Server和Service Manager是用户空间组件,而Binder Driver运行于内核空间。用户层的Client和Server基于Binder Driver和Service Manager进行通信。开发者通常无需了解Binder Driver与Service Manager的实现细节,只要按照规范设计实现自己的Client和Server组件即可。从Android应用程序设计的角度来看,进程通信机制如下图:
在系统安全设计方面,Android的进程通信机制设计具备优于传统Linux的重要优势。
Android应用基于权限机制,定义进程通信的权限,相比传统Linux IPC具有更细粒度的权限控制。
Binder进程间通信机制具备类型安全的优势。开发者在编译应用程序时,使用Android接口接描述语言(AIDL)定义交换数据的类型,确保进程间通信的数据不会溢出越界污染进程空间。
Binder通过Android的共享内存机制(Ashmem)实现高效率的进程通信,而不是采用传统的Linux/UNIX共享内存(Shared Memory),也具备特殊的安全含义。
Android在Binder进程通信机制中采用Android接口描述语言(AIDL)。AIDL同传统RPC中的IDL语言一样,根据描述可以生成代码,使两个进程通过内部通信进程进行交互。例如,在一个Activity(一个进程)中访问一个Service(另一个进程)的对象/服务,使用AIDL定义接口与参数并实现在进程间的传递。AIDL IPC的机制是基于接口的,类似于COM与Corba,但更为轻量级,使用代理类在客户端和实现层间传递值。
AIDL的接口定义与参数描述是类型安全的,与程序设计语言中的类型安全概念一致。Android应用程序使用java语言编写。Java语言就具备所谓的“类型安全”特性,是一种强类型化的编程语言, 它强制不同内容遵循规定的数据格式,进而防止错误或恶意应用。不完整的类型安全与边界检查机制极易受到内存污染或缓冲区溢出攻击,进而导致任意代码,甚至恶意代码的运行。但是,在C/C++程序设计中,允许未经类型检查的强制类型转换,而且,除非编程者专门编程进行边界检查,否则C语言本身不要求边界检查。实践证明,这些C/C++语言的灵活性恰成为恶意代码攻击的目标。Android系统原生库允许采用C/C++编程,存在一定的安全隐患,需要其他特殊技术加以防范。传统Linux的进程通信机制虽然有用户权限的限制,但缺少强制的类型安全。
由于类型安全的接口与数据描述,在接收方从其他进程接收数据时,可以充分检查安全性,确保其他进程发来的参数都在可接受的范围内,而不管调用者想要干什么,都可以防止进程间通信的数据溢出越界污染进程空间。
转载于:https://my.oschina.net/fhd/blog/351326