13、USB权限与安全机制:Android USB权限模型

说到USB开发,很多人第一反应是「写驱动、调协议」。但说实话,真正让我在项目里栽过跟头的,往往是权限和安全问题。我记得有一次,客户反馈说设备插上后App没反应,排查了半天——原来是权限弹窗被系统拦截了。嗯,从那以后,我对USB权限模型就格外上心。

13.1 Android USB权限模型

Android的USB权限模型,说白了就是一套「谁可以用、怎么用」的规则。系统不会让任何App随便碰USB设备,这背后涉及两个核心机制:用户授权和设备过滤。

13.1.1 用户授权机制

当你的App想访问一个USB设备时,系统会弹出一个对话框,问用户「是否允许该App访问这个USB设备?」。这个流程我习惯把它拆成三步:

  1. 广播发现:系统收到USB设备插拔事件,发送Intent广播
  2. 权限检查:App检查是否已有授权,如果没有则发起请求
  3. 用户确认:弹出系统对话框,用户点「允许」或「拒绝」

代码层面,我们通常这样请求权限:

// 获取USB设备
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

// 检查是否有权限
if (usbManager.hasPermission(device)) {
    // 直接打开设备通信
    openDevice(device);
} else {
    // 请求权限,会弹出系统对话框
    PendingIntent permissionIntent = PendingIntent.getBroadcast(
        this, 0, new Intent(ACTION_USB_PERMISSION), 0);
    usbManager.requestPermission(device, permissionIntent);
}
避坑指南:我曾经在requestPermission之后忘记注册BroadcastReceiver,结果用户点了「允许」但App毫无反应。记得一定要监听ACTION_USB_PERMISSION广播。

13.1.2 设备过滤机制

你想想看,如果每个USB设备插入都要弹窗,用户不得烦死?所以Android提供了设备过滤机制,让App只关心自己需要的设备。

设备过滤通过Intent Filter实现,在AndroidManifest.xml中声明:

<activity android:name=".UsbDeviceActivity">
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
    </intent-filter>
    <meta-data
        android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
        android:resource="@xml/device_filter"/>
</activity>

对应的device_filter.xml文件:

<resources>
    <usb-device vendor-id="1027" product-id="24577" />
    <usb-device vendor-id="1118" product-id="688" />
</resources>

这里vendor-id和product-id就是USB设备的VID和PID。我个人习惯在开发阶段先不设过滤,等调试稳定了再加,否则设备连不上你都不知道为什么。

13.2 USB设备白名单/黑名单实现

在实际项目中,尤其是企业级设备或定制ROM,我们经常需要实现白名单或黑名单机制。说白了就是:只允许某些设备工作,或者禁止某些设备接入。

13.2.1 白名单实现方案

白名单的思路是:只有列表中的设备才能被识别和使用。我曾在某安防项目中用过这个方案,效果不错。

实现方式有两种:

  • 应用层过滤:在App的device_filter.xml中只声明允许的设备VID/PID
  • 系统层过滤:修改framework层的UsbDeviceManager,在设备枚举时做过滤

系统层过滤的核心代码(以AOSP为例):

// frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
private boolean isDeviceAllowed(UsbDevice device) {
    // 读取白名单配置
    String[] whitelist = getResources().getStringArray(
        com.android.internal.R.array.config_usbWhitelist);
    
    for (String entry : whitelist) {
        String[] parts = entry.split(":");
        int vid = Integer.parseInt(parts[0]);
        int pid = Integer.parseInt(parts[1]);
        if (device.getVendorId() == vid && device.getProductId() == pid) {
            return true;
        }
    }
    return false;
}
注意:修改framework层代码需要重新编译系统镜像,普通App开发者不建议尝试。如果你只是做应用开发,用应用层过滤就够了。

13.2.2 黑名单实现方案

黑名单和白名单相反——除了列表中的设备,其他都能用。这个场景常见于企业管控,比如禁止员工使用USB存储设备。

黑名单的实现更简单,在系统层的UsbDeviceManager中加一个判断:

private boolean isDeviceBlocked(UsbDevice device) {
    String[] blacklist = getResources().getStringArray(
        com.android.internal.R.array.config_usbBlacklist);
    
    for (String entry : blacklist) {
        String[] parts = entry.split(":");
        int vid = Integer.parseInt(parts[0]);
        int pid = Integer.parseInt(parts[1]);
        if (device.getVendorId() == vid && device.getProductId() == pid) {
            return true;  // 命中黑名单,拒绝接入
        }
    }
    return false;
}

13.3 USB攻击面分析

做USB开发,不能只想着「怎么连上」,还得想想「坏人会怎么利用」。我见过太多只关注功能、忽视安全的项目,最后出了大问题。

13.3.1 BadUSB攻击

BadUSB是什么?简单说,就是USB设备伪装成键盘,向电脑发送恶意按键指令。你插上一个U盘,它实际上在模拟键盘输入,几秒钟就能植入木马。

为什么Android也危险?因为Android支持USB HID设备。一个伪装成键盘的BadUSB设备,可以在解锁状态下执行任意操作:

  • 打开浏览器下载恶意APK
  • 通过ADB命令安装应用
  • 修改系统设置

13.3.2 键盘记录器

键盘记录器是另一种常见的USB攻击手段。它本身就是一个USB设备,记录所有按键输入。在Android上,如果系统将USB键盘设备识别为输入设备,所有按键都会被记录。

我记得有个案例:某公司给员工配了Android平板做收银系统,结果有人插了个USB键盘记录器,所有银行卡密码都被截获了。嗯,这就是典型的「功能实现了,安全没跟上」。

13.4 安全防护措施

讲完了攻击,咱们聊聊怎么防。我总结了三道防线:SELinux策略、权限校验、应用层防护。

13.4.1 SELinux策略

SELinux是Android安全的核心。它通过强制访问控制(MAC)来限制进程的行为。对于USB设备,SELinux策略决定了哪些进程可以访问USB节点。

一个典型的USB相关SELinux策略示例:

# 允许system_app访问USB设备
allow system_app usb_device:chr_file rw_file_perms;

# 限制普通App直接访问USB节点
neverallow untrusted_app usb_device:chr_file open;

在定制ROM时,我建议这样配置:

  • 只给系统级App授予USB设备访问权限
  • 第三方App必须通过USB Manager API访问,不能直接操作设备节点
  • 对输入设备(键盘、鼠标)做特殊标记,限制其行为
核心原则:最小权限原则。不要给App超出需要的USB权限,尤其是HID设备权限。

13.4.2 权限校验

权限校验是第二道防线。在App层面,我们需要做两件事:

  1. 验证设备合法性:检查VID/PID是否在白名单中
  2. 验证设备类型:如果是HID设备,要特别小心

代码实现:

private boolean isDeviceTrusted(UsbDevice device) {
    // 1. 检查VID/PID
    if (!isInWhitelist(device.getVendorId(), device.getProductId())) {
        Log.w(TAG, "未授权的USB设备: " + device.getDeviceName());
        return false;
    }
    
    // 2. 检查设备类型
    UsbInterface usbInterface = device.getInterface(0);
    if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
        // HID设备需要额外验证
        return verifyHidDevice(device);
    }
    
    return true;
}

private boolean verifyHidDevice(UsbDevice device) {
    // 检查是否为键盘类HID设备
    // 如果是,需要用户二次确认
    // 这里可以弹出警告对话框
    return showSecurityWarning(device);
}

13.4.3 应用层防护建议

最后,给几个实战中的建议:

  • 不要自动连接:USB设备插入后,先验证再连接,不要自动打开通信通道
  • 限制HID设备:如果App不需要键盘输入,在过滤器中排除HID设备
  • 超时机制:USB通信设置超时,防止恶意设备占用资源
  • 日志审计:记录所有USB设备的接入和操作日志,方便事后追溯
我的经验:曾经有个项目,我们加了USB设备接入的实时告警。一旦检测到未授权的HID设备,立即锁屏并通知管理员。这个功能后来帮客户抓住了两次BadUSB攻击尝试。

13.5 知识体系总览

为了让你更直观地理解USB权限与安全机制的整体结构,我画了一张图:

Android USB权限与安全机制知识体系 USB权限模型 用户授权机制 设备过滤机制 白名单 / 黑名单 攻击面分析 安全防护措施 SELinux策略 权限校验 应用层防护

这张图把USB权限与安全的脉络理清楚了。从顶层的权限模型,到中间的白名单/黑名单和攻击面分析,再到底层的安全防护措施,层层递进。你写代码的时候,可以对照这张图,看看自己漏了哪一层。

好了,USB权限与安全这块就聊到这儿。记住一句话:功能做得再好,安全没跟上,迟早要出问题。我见过太多血的教训了。


公众号:蓝海资料掘金营,微信deep3321