Android系统采用多层次、纵深防御的安全架构,从Linux内核到应用层都实施了严格的安全控制。本章将深入剖析Android的安全机制,包括应用沙箱、权限系统、SELinux强制访问控制等核心组件,并与iOS等系统进行技术对比,帮助读者理解移动操作系统安全设计的最佳实践。
Android继承了Linux的多用户机制,但创新性地将其用于应用隔离。每个应用在安装时都会分配唯一的UID(用户ID),通常从10000开始递增。这种设计使得每个应用都运行在独立的Linux用户空间中。
UID分配策略:
UID分配实现:
mSettings.addUserIdLPw()分配UIDacquireAndRegisterNewAppIdLPw()获取未使用的UID多用户支持: Android支持多用户模式,实际UID计算公式:
实际UID = userId * 100000 + appId
其中userId是用户ID(主用户为0),appId是应用的基础ID。这使得同一应用在不同用户下有不同的UID。
UID权限映射: 每个UID对应特定的Linux权限组(GID),控制对系统资源的访问:
每个应用进程通过Zygote fork创建,继承了完整的安全上下文。Android的进程隔离不仅依赖Linux基础机制,还增加了多层安全增强。
1. 进程创建与隔离:
Zygote fork流程:
Process.start()请求创建新进程Zygote.forkAndSpecialize()handleChildProc()设置安全环境:
setuid()/setgid()setgroups()配置权限组capset()限制特权selinux_android_setcontext()内存隔离机制:
2. 文件系统隔离:
应用私有存储结构:
/data/data/<package_name>/
├── cache/ # 缓存目录,可被系统清理
├── code_cache/ # 运行时代码缓存
├── databases/ # SQLite数据库
├── files/ # 应用私有文件
├── lib/ # native库软链接
├── shared_prefs/ # SharedPreferences
└── no_backup/ # 不参与备份的文件
权限设置:
Context.MODE_PRIVATE创建的文件仅本应用可访问Context.MODE_WORLD_READABLE/WRITEABLE已废弃(安全原因)外部存储隔离:
/storage/emulated/0/Android/data/<package_name>//storage/emulated/0/Android/media/<package_name>//storage/emulated/0/Android/obb/<package_name>/3. IPC安全限制:
Binder安全检查:
Binder.getCallingUid(): 获取调用者UIDBinder.getCallingPid(): 获取调用者PIDBinder.getCallingUserHandle(): 获取调用者用户Binder.clearCallingIdentity(): 临时切换到自己的身份Binder.restoreCallingIdentity(): 恢复调用者身份权限enforcement位置:
enforceCallingPermission()@EnforcePermissionIPCThreadState::getCallingUid()4. 其他隔离机制:
命名空间隔离:
资源限制:
Android存储模型经历了重大变革,从早期的开放模型逐步演进到严格的隔离模型。
存储架构基础:
存储类型分类:
/data/data/<package_name>//data/app/<package_name>//data/user/<userId>/<package_name>//storage/emulated/0//storage/<sdcard_id>//storage/<usb_id>/Android 10之前的存储模型:
FUSE实现机制:
权限控制:
READ_EXTERNAL_STORAGE:
- 读取/sdcard所有文件
- 自动包含媒体文件访问
WRITE_EXTERNAL_STORAGE:
- 写入/sdcard任意位置
- 隐含READ权限
- 可创建任意目录结构
安全问题:
Android 10 Scoped Storage:
核心设计理念:
实现机制:
Context.getExternalFilesDir() -> /storage/emulated/0/Android/data/<pkg>/files/
Context.getExternalCacheDir() -> /storage/emulated/0/Android/data/<pkg>/cache/
Context.getExternalMediaDirs() -> /storage/emulated/0/Android/media/<pkg>/
ACTION_OPEN_DOCUMENT: 选择单个文件ACTION_OPEN_DOCUMENT_TREE: 选择目录树ACTION_CREATE_DOCUMENT: 创建新文件兼容性措施:
requestLegacyExternalStorage="true": 临时退出(targetSdk<30)preserveLegacyExternalStorage="true": 升级时保留旧行为Android 11+增强:
新增限制:
ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION请求MediaStore.createWriteRequest()Android 12+进一步改进:
应用存储访问指示器:
媒体权限细分:
READ_MEDIA_IMAGES: 仅图片访问
READ_MEDIA_VIDEO: 仅视频访问
READ_MEDIA_AUDIO: 仅音频访问
替代原有的READ_EXTERNAL_STORAGE
Android 13+最新变化:
照片选择器:
ACTION_PICK_IMAGES意图细粒度媒体权限:
Android实现了细粒度的网络访问控制,从应用层到内核层都有相应的安全机制。
1. 网络权限控制:
INTERNET权限机制:
af_inet.c中的权限检查其他网络相关权限:
ACCESS_NETWORK_STATE: 读取网络连接状态
ACCESS_WIFI_STATE: 读取WiFi状态信息
CHANGE_NETWORK_STATE: 修改网络连接
CHANGE_WIFI_STATE: 修改WiFi状态
ACCESS_FINE_LOCATION: WiFi/基站定位需要
BIND_VPN_SERVICE: 创建VPN服务
2. 网络流量控制(Netd守护进程):
UID级别的iptables规则:
# 按UID允许/拒绝网络访问
iptables -A fw_OUTPUT -m owner --uid-owner <uid> -j ACCEPT/DROP
# 按UID路由到特定网络接口
ip rule add uidrange <start>-<end> table <table_id>
# 带宽限制
iptables -A bw_costly_<iface> -m owner --uid-owner <uid> -j bw_penalty_box
网络策略实施:
3. VPN隔离机制:
Per-app VPN:
实现原理:
1. 创建tun接口:/dev/tun
2. 设置路由规则:
ip rule add uidrange <uid> table <vpn_table>
ip route add default dev tun0 table <vpn_table>
3. iptables标记:
iptables -t mangle -A OUTPUT -m owner --uid-owner <uid> -j MARK --set-mark <vpn_mark>
Always-on VPN:
4. 网络安全增强:
DNS-over-TLS (Android 9+):
网络安全配置 (Android 7+):
<!-- network_security_config.xml -->
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
<domain-config>
<domain includeSubdomains="true">example.com</domain>
<pin-set expiration="2025-01-01">
<pin digest="SHA-256">base64==</pin>
</pin-set>
</domain-config>
</network-security-config>
证书固定(Certificate Pinning):
5. 网络隔离高级特性:
多网络API (Android 5+):
网络切片(Network Slicing):
企业网络隔离:
6. 网络审计与监控:
流量统计:
网络日志:
防火墙日志:
# 启用iptables日志
iptables -A INPUT -j LOG --log-prefix "[FW_INPUT] "
# 查看日志
logcat -s netd
Android权限系统基于最小权限原则,通过细粒度的权限控制保护用户隐私和系统安全。
权限层级体系:
基本权限级别:
| **signature | privileged** (旧称signatureOrSystem) |
权限保护级别属性:
<!-- 在framework-res/AndroidManifest.xml中定义 -->
<permission android:name="android.permission.CAMERA"
android:permissionGroup="android.permission-group.CAMERA"
android:protectionLevel="dangerous"
android:label="@string/permlab_camera"
android:description="@string/permdesc_camera" />
权限实现架构:
权限存储与管理:
/data/system/packages.xml:应用权限授予状态/data/misc_de/0/apexdata/com.android.permission/:运行时权限状态<!-- packages.xml 示例 -->
<package name="com.example.app" >
<perms>
<item name="android.permission.CAMERA" granted="true" flags="0" />
<item name="android.permission.INTERNET" granted="true" flags="0" />
</perms>
</package>
权限检查机制:
检查流程:
Context.checkSelfPermission(permission)
→ ContextImpl.checkPermission(permission, pid, uid)
→ ActivityManager.checkPermission(permission, pid, uid)
ActivityManagerService.checkPermission()
→ PackageManager.checkUidPermission(permission, uid)
→ PermissionManagerService.checkUidPermission()
Context.enforcePermission(permission, pid, uid, message)
→ 抛出SecurityException如果无权限
性能优化:
权限代理机制:
AppOps系统:
// AppOps操作示例
AppOpsManager.noteOp(AppOpsManager.OP_CAMERA, uid, packageName)
→ 记录权限使用
→ 可能返回AppOpsManager.MODE_IGNORED禁用操作
权限委托:
Intent.FLAG_GRANT_READ_URI_PERMISSION运行时权限彻底改变了危险权限的授予方式,大幅提升了用户对隐私的控制能力。
核心设计理念:
实现架构:
系统组件:
权限请求流程:
1. 应用调用:requestPermissions(permissions[], requestCode)
↓
2. ActivityManagerService处理请求
↓
3. 启动PermissionController中的GrantPermissionsActivity
↓
4. 用户做出选择(允许/拒绝/仅在使用期间允许)
↓
5. PermissionManagerService更新权限状态
↓
6. 回调应用的onRequestPermissionsResult()
权限状态管理:
权限授予状态:
权限标志:
// PermissionInfo.java中的标志
FLAG_COSTS_MONEY = 1<<0; // 可能产生费用
FLAG_REMOVED = 1<<1; // 已移除的权限
FLAG_HARD_RESTRICTED = 1<<2; // 硬性限制权限
FLAG_SOFT_RESTRICTED = 1<<3; // 软性限制权限
FLAG_IMMUTABLY_RESTRICTED = 1<<4; // 不可变限制
持久化存储:
<!-- runtime-permissions.xml -->
<runtime-permissions fingerprint="...">
<pkg name="com.example.app">
<perm name="android.permission.CAMERA" granted="true" />
<perm name="android.permission.LOCATION" granted="false" />
</pkg>
</runtime-permissions>
权限组机制:
权限组定义:
<permission-group android:name="android.permission-group.LOCATION"
android:icon="@drawable/perm_group_location"
android:label="@string/permgroup_location"
android:description="@string/permgroup_location_description"
android:priority="400" />
权限组授予规则:
高级特性:
自动撤销机制 (Android 11+):
// 应用长时间未使用时自动撤销权限
val unusedAppRestrictionsStatus =
packageManager.getUnusedAppRestrictionsStatus()
when (unusedAppRestrictionsStatus) {
FEATURE_NOT_AVAILABLE -> // 功能不可用
DISABLED -> // 已禁用自动撤销
API_30_BACKPORT, API_31 -> // 启用中
}
单次授权 (Android 11+):
权限使用说明:
// 判断是否需要显示使用说明
if (shouldShowRequestPermissionRationale(permission)) {
// 显示为什么需要这个权限
// 然后再次请求
}
兼容性处理:
targetSdkVersion影响:
= 23:必须实现运行时权限逻辑
降级处理:
// 旧应用在权限被撤销后
// 相关API返回空值或默认值
// 例如:getLastKnownLocation()返回null
Android引入了更透明的权限使用跟踪,让用户能够清楚了解应用如何使用授予的权限。
1. 位置权限改革:
三级位置权限体系:
位置精度降级:
// 用户可以选择仅授予粗略位置
// 即使应用请求ACCESS_FINE_LOCATION
LocationManager locationManager = getSystemService(LocationManager.class);
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
// 可能被系统降级为网络位置
}
2. 权限使用指示器:
实时指示器 (Android 12+):
指示器实现:
// PermissionUsageHelper.java
public class PermissionUsageHelper {
// 监听权限使用
private void startListeningForPermissionUsage() {
mAppOpsManager.startWatchingActive(
OPSTR_CAMERA, OPSTR_RECORD_AUDIO,
mExecutor, mActiveOpListener);
}
// 更新指示器状态
private void updateIndicatorState(String op, boolean active) {
if (active) {
showIndicator(op);
} else {
hideIndicator(op);
}
}
}
3. 隐私仪表板:
功能特性:
数据收集:
// AppOpsService记录使用事件
public class AppOpsService {
private void noteOperationUnchecked(int code, int uid,
String packageName, String attributionTag) {
// 记录操作时间戳
Op op = getOpLocked(ops, code, uid, true);
op.noteOpCount++;
op.time[uidState.state] = System.currentTimeMillis();
// 发送到PermissionController
mHistoricalRegistry.noteOp(code, uid, packageName);
}
}
UI展示:
4. 权限使用审计API:
开发者API:
// 获取权限使用记录
AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
List<AppOpsManager.PackageOps> packages =
appOpsManager.getPackagesForOps(new int[]{OP_CAMERA});
for (PackageOps pkg : packages) {
for (OpEntry op : pkg.getOps()) {
long lastAccessTime = op.getLastAccessTime();
int accessCount = op.getAccessCount();
}
}
系统审计日志:
# 查看权限使用日志
adb shell dumpsys appops
# 输出示例
Uid 10123:
Package com.example.app:
CAMERA (allow / switch COARSE_LOCATION=allow):
Access: [fg-s] 2024-01-15 10:30:15.123 (-5m32s)
Reject: [bg] 2024-01-15 09:15:00.000 (-1h20m47s)
5. 数据访问审计 (Android 11+):
数据访问记录:
// 应用可以记录自己的数据访问
AppOpsManager.OnOpNotedCallback callback = new OnOpNotedCallback() {
@Override
public void onOpNoted(String op, String attributionTag) {
Log.d(TAG, "Permission " + op + " was used");
}
};
appOpsManager.setOnOpNotedCallback(executor, callback);
归因标签(Attribution Tags):
<!-- AndroidManifest.xml -->
<attribution
android:tag="location_feature"
android:label="@string/location_feature_label" />
// 使用归因标签
Context attributionContext =
createAttributionContext("location_feature");
LocationManager locationManager =
attributionContext.getSystemService(LocationManager.class);
// 此时位置访问会记录归因标签
6. 自动重置机制:
未使用应用权限自动重置 (Android 11+):
// 检查自动重置状态
PackageManager pm = getPackageManager();
if (pm.isAutoRevokeWhitelisted()) {
// 应用已豁免自动重置
}
// 引导用户禁用自动重置
Intent intent = new Intent(
Intent.ACTION_AUTO_REVOKE_PERMISSIONS,
Uri.parse("package:" + getPackageName()));
startActivity(intent);
某些敏感操作需要特殊权限,这些权限不能通过普通的运行时权限流程授予。
1. 系统设置权限:
SYSTEM_ALERT_WINDOW (悬浮窗):
// 检查权限
if (!Settings.canDrawOverlays(this)) {
// 请求权限
Intent intent = new Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
}
// 使用悬浮窗
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
windowManager.addView(floatingView, params);
WRITE_SETTINGS (修改系统设置):
// 检查权限
if (!Settings.System.canWrite(this)) {
Intent intent = new Intent(
Settings.ACTION_MANAGE_WRITE_SETTINGS,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
}
// 修改设置
Settings.System.putInt(
getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS,
brightness);
REQUEST_INSTALL_PACKAGES (安装应用):
// Android 8.0+需要此权限
if (!getPackageManager().canRequestPackageInstalls()) {
Intent intent = new Intent(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
}
// 安装APK
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setDataAndType(apkUri,
"application/vnd.android.package-archive");
installIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(installIntent);
2. 辅助功能权限:
AccessibilityService:
<!-- 服务声明 -->
<service android:name=".MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config" />
</service>
<!-- accessibility_service_config.xml -->
<accessibility-service
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagReportViewIds|flagRetrieveInteractiveWindows"
android:canRetrieveWindowContent="true"
android:canPerformGestures="true"
android:description="@string/accessibility_service_description" />
能力范围:
3. 设备管理权限:
DeviceAdminReceiver:
<!-- 设备管理器声明 -->
<receiver android:name=".DeviceAdminReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
android:resource="@xml/device_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
// 激活设备管理器
ComponentName adminComponent = new ComponentName(this, DeviceAdminReceiver.class);
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, adminComponent);
startActivityForResult(intent, REQUEST_ENABLE);
// 使用管理功能
DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
dpm.lockNow(); // 锁定设备
dpm.resetPassword("newpassword", 0); // 重置密码
dpm.wipeData(0); // 清除数据
DeviceOwner/ProfileOwner:
# 设置DeviceOwner(需要设备未配置)
adb shell dpm set-device-owner com.example.app/.DeviceAdminReceiver
# 设置ProfileOwner(Work Profile)
adb shell dpm set-profile-owner com.example.app/.DeviceAdminReceiver --user 10
企业管理能力:
// DeviceOwner可以:
// 1. 卸载任意应用
dpm.setUninstallBlocked(admin, packageName, true);
// 2. 隐藏应用
dpm.setApplicationHidden(admin, packageName, true);
// 3. 设置系统更新策略
dpm.setSystemUpdatePolicy(admin, SystemUpdatePolicy.createAutomaticInstallPolicy());
// 4. 设置全局HTTP代理
dpm.setGlobalProxy(admin, proxyInfo);
// 5. 禁用相机
dpm.setCameraDisabled(admin, true);
4. 通知监听权限:
NotificationListenerService:
<service android:name=".NotificationListener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
public class NotificationListener extends NotificationListenerService {
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
// 获取通知内容
String packageName = sbn.getPackageName();
CharSequence title = sbn.getNotification().extras
.getCharSequence(Notification.EXTRA_TITLE);
CharSequence text = sbn.getNotification().extras
.getCharSequence(Notification.EXTRA_TEXT);
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
// 通知被移除
}
}
5. VPN服务权限:
VpnService:
public class MyVpnService extends VpnService {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 配置VPN
Builder builder = new Builder();
builder.setSession("MyVPN")
.addAddress("10.0.0.2", 24)
.addDnsServer("8.8.8.8")
.addRoute("0.0.0.0", 0)
.setMtu(1500);
// 允许/禁止特定应用
builder.addAllowedApplication("com.example.app");
builder.addDisallowedApplication("com.example.blocked");
ParcelFileDescriptor interface = builder.establish();
// 处理VPN流量
return START_STICKY;
}
}
6. 特殊权限管理:
权限查询API:
// 获取所有特殊权限应用
PackageManager pm = getPackageManager();
List<PackageInfo> packages = pm.getPackagesHoldingPermissions(
new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW},
PackageManager.GET_PERMISSIONS);
// 检查多个特殊权限
AppOpsManager appOps = getSystemService(AppOpsManager.class);
int mode = appOps.checkOpNoThrow(
AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW,
uid, packageName);
boolean granted = mode == AppOpsManager.MODE_ALLOWED;
用户体验优化:
// 提示用户为什么需要特殊权限
private void requestSpecialPermission() {
new AlertDialog.Builder(this)
.setTitle("需要悬浮窗权限")
.setMessage("为了显示悬浮计时器,需要您授予悬浮窗权限")
.setPositiveButton("去设置", (dialog, which) -> {
Intent intent = new Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
})
.setNegativeButton("取消", null)
.show();
}
Android从4.3开始引入SELinux,5.0起全面启用enforcing模式:
核心概念:
Android特定实现:
SELinux策略分布在多个位置:
/system/etc/selinux/ # 平台策略
/vendor/etc/selinux/ # 厂商策略
/odm/etc/selinux/ # ODM策略
关键策略文件:
进程域转换:
seclabel设置域type_transition规则实现自动域转换文件类型强制:
Project Treble引入了更严格的SELinux边界:
Android沙箱:
iOS沙箱:
Android:
iOS:
Android Binder:
iOS XPC/Mach:
Android:
iOS:
Android安全模型采用纵深防御策略,主要包括:
与iOS相比,Android提供了更灵活但相对复杂的安全模型。理解这些机制对于开发安全的Android应用和进行安全研究至关重要。
UID分配机制
Android为每个应用分配UID的范围是什么?系统应用和普通应用的UID有何区别?
Hint: 查看Process.java中的UID常量定义
权限级别识别
给定权限android.permission.CAMERA和android.permission.ACCESS_NETWORK_STATE,分别属于什么级别?这对用户授权有什么影响?
Hint: 考虑哪些权限需要运行时请求
SELinux上下文理解
解释SELinux上下文u:r:untrusted_app:s0:c512,c768的含义,每部分代表什么?
Hint: user:role:type:sensitivity:categories
存储访问演进
列举Android 10 Scoped Storage相比传统存储模型的三个主要变化。
Hint: 考虑直接路径访问、权限需求和API变化
沙箱逃逸分析
设计一个理论上的Android应用沙箱逃逸场景,需要绕过哪些安全机制?现代Android如何防御此类攻击?
Hint: 考虑多层防御体系
权限提升路径
一个普通应用如何合法地获得系统级别的能力?分析至少两种不同的路径及其安全含义。
Hint: 考虑Android企业功能和辅助功能
SELinux策略设计
为一个需要访问摄像头硬件的自定义HAL服务设计SELinux策略,需要定义哪些规则?
Hint: 考虑文件访问、binder通信、属性访问
跨平台安全对比
分析Android的共享UID机制与iOS的App Group在安全性和功能性上的差异,哪种设计更优?
Hint: 考虑隔离性、灵活性和向后兼容性
ContextCompat.checkSelfPermission()Binder.getCallingUid()而不清空调用身份Binder.clearCallingIdentity()确保正确的安全上下文