Android 应用中广播权限未指定风险与解决方案
1. 背景
在Android应用中,广播(Broadcast)是一种重要的通信机制,应用可以通过广播与系统或其他应用进行通信。然而,如果在发送广播时未指定接收者的权限,可能会导致应用暴露于安全风险之下。未授权的接收者可能会拦截这些广播,甚至可能执行恶意操作。
在安全检查中,发现了一个问题:应用存在“Receiver权限未指定风险”。这个问题主要涉及广播发送时未指定接收者权限,可能导致广播被未授权的应用接收,进而可能造成数据泄露或其他安全问题。
检测步骤:
- 反编译APK文件。
- 扫描smali代码,查找发送广播是否未指定接收者权限。
2. 识别问题
在原始代码中,广播在发送时没有指定权限,这意味着任何应用都可以接收这个广播:
class Messenger extends BroadcastReceiver {
public static void send(Context context, String suffix) {
Intent broadcast = new Intent(AndPermission.bridgeAction(context, suffix));
context.sendBroadcast(broadcast); // 未指定权限
}
// 其他代码省略
}
这种实现存在安全隐患,尤其是在广播中传递敏感信息时。因此,解决此问题的关键在于,确保在发送广播时指定接收者权限,限制接收广播的应用范围。
3. 解决方案一:使用自定义权限限制广播接收
步骤1:定义自定义权限
首先,在AndroidManifest.xml
中定义自定义权限,这个权限将用于限制哪些应用能够接收广播。
<permission
android:name="${applicationId}.andpermission.bridge"
android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.andpermission.bridge" />
protectionLevel="signature"
:这个设置确保只有与应用具有相同签名的应用才能使用该权限。${applicationId}
:在Gradle构建时会自动替换为应用的包名。
步骤2:动态注册广播接收器
由于广播的action
是动态生成的,无法在AndroidManifest.xml
中静态注册,因此需要在代码中动态注册广播接收器。
class Messenger extends BroadcastReceiver {
public static void send(Context context, String suffix) {
// 生成广播的动作字符串
Intent broadcast = new Intent(AndPermission.bridgeAction(context, suffix));
// 发送广播并指定权限
context.sendBroadcast(broadcast, AndPermission.bridgeAction(context, null));
}
private final Context mContext;
private final Callback mCallback;
public Messenger(Context context, Callback callback) {
this.mContext = context;
this.mCallback = callback;
}
public void register(String suffix) {
// 动态生成广播动作字符串,并注册接收器
IntentFilter filter = new IntentFilter(AndPermission.bridgeAction(mContext, suffix));
mContext.registerReceiver(this, filter);
}
public void unRegister() {
// 注销广播接收器
mContext.unregisterReceiver(this);
}
@Override
public void onReceive(Context context, Intent intent) {
// 处理接收到的广播
mCallback.onCallback();
}
public interface Callback {
void onCallback();
}
}
代码解析
send
方法:现在在发送广播时,指定了权限AndPermission.bridgeAction(context, null)
,以确保只有具有相同权限的应用才能接收广播。- 动态注册接收器:由于广播的动作是动态生成的,接收器必须通过代码在运行时注册,而不是在
AndroidManifest.xml
中静态注册。
步骤3:实际使用示例
以下是如何在应用中使用Messenger
类的示例:
// 创建Messenger实例并注册接收器
Messenger messenger = new Messenger(context, new Messenger.Callback() {
@Override
public void onCallback() {
// 接收到广播后的处理逻辑
Log.d("Messenger", "Broadcast received!");
}
});
messenger.register("dynamic_suffix");
// 发送广播
Messenger.send(context, "dynamic_suffix");
// 在不需要时注销接收器
messenger.unRegister();
4. 解决方案二:静态注册并限制广播接收器
步骤1:在AndroidManifest.xml
中注册接收器
如果广播动作不是动态生成的,可以选择在AndroidManifest.xml
中静态注册接收器,并使用权限来限制接收者。
<receiver android:name=".Messenger">
<intent-filter>
<action android:name="com.yanzhenjie.permission.bridge.ACTION_SUFFIX" />
</intent-filter>
<permission android:name="${applicationId}.andpermission.bridge" />
</receiver>
- 静态注册:这种方式适用于广播动作是固定的情况。
action
的值需要与发送广播时使用的动作一致。
步骤2:发送广播时指定权限
即使使用静态注册,发送广播时仍然需要指定权限:
public static void send(Context context, String suffix) {
Intent broadcast = new Intent("com.yanzhenjie.permission.bridge.ACTION_SUFFIX");
context.sendBroadcast(broadcast, "com.yanzhenjie.permission.bridge.PERMISSION");
}
注意:
- 在发送广播时,动作字符串(
"com.yanzhenjie.permission.bridge.ACTION_SUFFIX"
)必须与AndroidManifest.xml
中注册的action
一致。 - 使用自定义权限
com.yanzhenjie.permission.bridge.PERMISSION
来限制接收广播的应用。
5. 解决方案三:使用LocalBroadcastManager
进行本地广播
步骤1:使用LocalBroadcastManager
发送广播
LocalBroadcastManager
提供了一个在应用内部发送和接收广播的机制。这种广播不需要担心权限问题,因为它不会离开应用程序的进程空间,因此只能被同一应用内的组件接收。
class Messenger extends BroadcastReceiver {
public static void send(Context context, String suffix) {
Intent broadcast = new Intent(AndPermission.bridgeAction(context, suffix));
LocalBroadcastManager.getInstance(context).sendBroadcast(broadcast);
}
private final Context mContext;
private final Callback mCallback;
public Messenger(Context context, Callback callback) {
this.mContext = context;
this.mCallback = callback;
}
public void register(String suffix) {
IntentFilter filter = new IntentFilter(AndPermission.bridgeAction(mContext, suffix));
LocalBroadcastManager.getInstance(mContext).registerReceiver(this, filter);
}
public void unRegister() {
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(this);
}
@Override
public void onReceive(Context context, Intent intent) {
mCallback.onCallback();
}
public interface Callback {
void onCallback();
}
}
代码解析
- 发送广播:通过
LocalBroadcastManager
发送的广播只能在应用内部接收,因此完全避免了权限管理的问题。 - 注册和注销接收器:同样,通过
LocalBroadcastManager
来注册和注销广播接收器,确保接收器只能接收本应用内的广播。
步骤2:实际使用示例
// 创建Messenger实例并注册接收器
Messenger messenger = new Messenger(context, new Messenger.Callback() {
@Override
public void onCallback() {
// 接收到广播后的处理逻辑
Log.d("Messenger", "Local Broadcast received!");
}
});
messenger.register("local_suffix");
// 发送本地广播
Messenger.send(context, "local_suffix");
// 在不需要时注销接收器
messenger.unRegister();
6. 讨论与总结
在解决“Receiver权限未指定风险”时,有三种主要方案:
- 动态注册接收器并指定权限:适用于广播动作是动态生成的场景。需要在代码中动态注册接收器,并在发送广播时指定权限。
- 静态注册接收器并限制接收权限:适用于广播动作是固定的场景。在
AndroidManifest.xml
中静态注册接收器,并通过权限限制接收者。 - 使用
LocalBroadcastManager
进行本地广播:适用于广播只在应用内传递的场景。使用LocalBroadcastManager
可以完全避免权限问题,因为广播不会离开应用程序的进程空间。