20. Broadcast Receiver安全:有序广播、权限保护、粘性广播风险
Broadcast Receiver,说白了就是Android系统里的“消息喇叭”。
任何应用都能往系统里扔一条广播,任何注册了接收器的应用也都能听到。这听起来很方便,对吧?但方便的背后,藏着不少安全隐患。我见过太多应用因为广播处理不当,被恶意应用钻了空子。
今天我们就来聊聊广播接收器的三大安全要点:有序广播、权限保护,还有那个已经被废弃但依然值得警惕的粘性广播。
20.1 有序广播 vs 无序广播
先说说广播的两种类型。无序广播,也叫普通广播,是异步的。发送者把广播扔出去,所有接收者几乎同时收到,互不干扰。但有序广播不一样,它像一场接力赛——广播会按优先级依次传递给每个接收者,而且每个接收者都可以截断它,不让它继续往下传。
核心区别:
- 无序广播:所有接收者同时收到,无法截断,无法修改数据
- 有序广播:按优先级顺序传递,可截断,可修改广播数据
我做过一个项目,需要实现一个“紧急消息”功能。系统发出警报后,只有最高优先级的接收者才能处理,处理完就截断广播,防止其他应用干扰。这种场景,有序广播就是最佳选择。
来看一个有序广播的发送示例:
// 发送有序广播
Intent intent = new Intent("com.example.ACTION_EMERGENCY");
intent.putExtra("level", 5);
sendOrderedBroadcast(intent, null); // 第二个参数是接收者权限
接收者需要设置优先级:
<receiver android:name=".EmergencyReceiver"
android:permission="android.permission.RECEIVE_EMERGENCY">
<intent-filter android:priority="1000">
<action android:name="com.example.ACTION_EMERGENCY" />
</intent-filter>
</receiver>
优先级越高,越早收到广播。如果某个接收者调用了 abortBroadcast(),后面的接收者就收不到了。
注意:有序广播的优先级只是相对顺序,不是绝对保证。系统可能会根据进程状态做一些调整。别把业务逻辑完全押在优先级上。
20.2 权限保护:给广播加把锁
广播默认是开放的。任何应用都能发,任何应用都能收。这就像你家大门没锁,谁都能进来坐坐。你想想看,这多危险?
我建议,只要广播涉及敏感数据或关键操作,就必须加权限保护。权限保护分两种:
- 发送方权限:只有持有指定权限的应用才能发送该广播
- 接收方权限:只有持有指定权限的应用才能接收该广播
举个例子,你的应用要广播用户的登录状态:
// 发送方:只有持有 MY_PERMISSION 的应用才能发送
sendBroadcast(new Intent("com.example.LOGIN_STATUS"), "com.example.permission.MY_PERMISSION");
接收方在清单文件中声明:
<receiver android:name=".LoginStatusReceiver"
android:permission="com.example.permission.MY_PERMISSION">
<intent-filter>
<action android:name="com.example.LOGIN_STATUS" />
</intent-filter>
</receiver>
这样,没有权限的应用既发不了也收不到这个广播。我曾经在一个金融类项目里,就因为漏了权限保护,导致用户交易信息被第三方应用截获。嗯,从那以后,我再也不敢省略这一步了。
避坑指南:自定义权限时,记得设置 protectionLevel 为 signature 或 signatureOrSystem。如果设为 normal,任何应用都能申请,等于没保护。
20.3 粘性广播:一个被废弃的危险品
粘性广播,英文叫 Sticky Broadcast。它的特点是:广播发送后,即使接收者还没注册,等它注册时也能收到最后一条广播。
听起来很神奇对吧?但问题也出在这里。
粘性广播会把数据一直留在系统里,任何应用都能通过 registerReceiver() 拿到这些数据。这意味着什么?意味着你的敏感信息可能被任何应用读取,而且你根本不知道谁在读。
从 Android 5.0(API 21)开始,粘性广播就被正式废弃了。但我在一些老旧项目里还是见过它的影子。
// 发送粘性广播(已废弃,不推荐使用)
sendStickyBroadcast(new Intent("com.example.STICKY_ACTION"));
接收方可以这样获取:
// 任何应用都能获取粘性广播的数据
Intent stickyIntent = registerReceiver(null, new IntentFilter("com.example.STICKY_ACTION"));
if (stickyIntent != null) {
String data = stickyIntent.getStringExtra("secret");
}
你看,连 registerReceiver 的第一个参数都可以传 null,就是为了读取粘性广播。这简直就是给恶意应用开了一扇后门。
强烈建议:不要在新项目中使用粘性广播。如果必须保留历史数据,改用 LocalBroadcastManager 或 LiveData 替代。
20.4 安全最佳实践总结
说了这么多,我总结几条实用的建议:
- 能用局部广播就别用全局广播。LocalBroadcastManager 只在应用内部传递,安全又高效。
- 敏感广播必须加权限。不管是发送还是接收,权限保护是底线。
- 有序广播要谨慎截断。截断广播会影响其他接收者,别滥用。
- 永远不要用粘性广播。它已经被废弃,而且存在严重的安全风险。
- 动态注册记得解注册。在 Activity 或 Fragment 的 onDestroy 中调用 unregisterReceiver,防止内存泄漏。
最后,我画了一张图,帮你理清广播安全的整体脉络:
广播接收器看似简单,但安全细节不少。记住一句话:不要相信任何外部广播的来源。无论是系统广播还是第三方应用广播,都要当作不可信数据来处理。
好了,这一章的内容就到这里。希望这些经验能帮你少踩几个坑。
公众号:蓝海资料掘金营,微信deep3321