安全性

本指南中的功能介绍了您可以在设备政策控制器 (DPC) 应用中实现的安全管理功能。本文档包含代码示例,您还可以将 Test DPC 应用用作 Android 企业功能示例代码的源代码。

DPC 应用可以在个人设备上以资料所有者模式运行,在全代管式设备上以设备所有者模式运行。下表列出了 DPC 在资料所有者模式或设备所有者模式下运行时可以使用哪些功能:

功能 资料所有者 设备所有者
禁止访问应用
屏蔽来自未知来源的应用
在 Google Play 中限制帐号
启用企业恢复出厂设置保护
监控企业进程日志和远程 bug 报告
授予客户端证书访问权限和移除对客户端证书的访问权限
安全密码重置
工作资料安全性挑战

停用应用访问权限

如果组织想禁止员工在一天中的特定时间段或一周中的某几天通过 Android 设备玩游戏或观看 YouTube 视频,DPC 可暂时停用对应用的访问权限。

如需停用对应用的访问权限,在设备所有者模式或资料所有者模式下运行的 DPC 会配置 setPackagesSuspended(),然后所选应用就像被停用一样(Google 启动器会将应用灰显)。当用户点按该应用时,系统会显示一个系统对话框,说明该应用已暂停。

应用被暂停后,无法启动 activity,并且会禁止向软件包发送通知。已暂停的软件包不会显示在概览屏幕中,不能显示对话框(包括消息框和信息提示控件),无法播放音频或振动设备。

启动器可以通过调用 isPackageSuspended() 方法来了解应用是否已暂停。如需详细了解如何配置应用暂停,请参阅 setPackagesSuspended

屏蔽来自未知来源的应用

不是从 Google Play(或其他受信任的应用商店)安装的应用称为来自未知来源的应用。如果用户安装这些应用,设备和数据可能会面临增加风险。

为防止有人安装来自未知来源的应用,完全受管设备和工作资料的管理员组件可以添加 DISALLOW_INSTALL_UNKNOWN_SOURCES 用户限制。

工作资料设备级限制

当工作资料的管理员添加 DISALLOW_INSTALL_UNKNOWN_SOURCES 时,此限制仅适用于工作资料。但是,工作资料管理员可以通过为 Google Play 设置托管配置来施加设备范围的限制。如果已安装的 Google Play 应用版本为 80812500 或更高版本,则设备级限制适用于 Android 8.0(或更高版本)。

若要限制应用安装在 Google Play 上,请按照以下步骤操作:

  1. 为 Google Play 软件包 com.android.vending 设置托管配置软件包。
  2. 在该 bundle 中,为 verify_apps:device_wide_unknown_source_block 键输入一个布尔值。
  3. 添加 ENSURE_VERIFY_APPS 用户限制。

以下示例展示了如何检查 Google Play 是否支持此设置并将其值设置为 true

Kotlin

internal val DEVICE_WIDE_UNKNOWN_SOURCES = "verify_apps:device_wide_unknown_source_block"
internal val GOOGLE_PLAY_APK = "com.android.vending"

// ...

// Add the setting to Google Play's existing managed config. Supported in
// Google Play version 80812500 or higher--older versions ignore unsupported
// settings.
val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
var existingConfig = dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK)
val newConfig = Bundle(existingConfig)
newConfig.putBoolean(DEVICE_WIDE_UNKNOWN_SOURCES, true)
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig)

// Make sure that Google Play Protect verifies apps.
dpm.addUserRestriction(adminName, UserManager.ENSURE_VERIFY_APPS)
dpm.addUserRestriction(adminName, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)

Java

static final String DEVICE_WIDE_UNKNOWN_SOURCES =
    "verify_apps:device_wide_unknown_source_block";
static final String GOOGLE_PLAY_APK = "com.android.vending";

// ...


// Add the setting to Google Play's existing managed config. Supported in
// Google Play version 80812500 or higher--older versions ignore unsupported
// settings.
DevicePolicyManager dpm =
    (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
Bundle existingConfig =
    dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK);
Bundle newConfig = new Bundle(existingConfig);
newConfig.putBoolean(DEVICE_WIDE_UNKNOWN_SOURCES, true);
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig);

// Make sure that Google Play Protect verifies apps.
dpm.addUserRestriction(adminName, UserManager.ENSURE_VERIFY_APPS);
dpm.addUserRestriction(adminName, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);

系统设置中的界面仍处于活动状态,但系统会阻止应用安装。此限制会影响以后的安装 - 之前安装的应用会保留在设备上。设备用户可以继续使用 Android 调试桥 (adb) 将应用安装到个人资料中。

如需详细了解未知来源,请参阅其他分发选项

在 Google Play 中限制账号

有时,组织可能希望允许用户添加个人 Google 帐号(例如,在 Gmail 中阅读邮件),但不希望通过个人帐号安装应用。您的 DPC 可以设置用户可以在 Google Play 中使用的帐号列表。

完全受管设备或工作资料的管理员组件可以通过为 Google Play 设置托管配置来限制帐号。如果已安装的 Google Play 应用版本为 80970100 或更高版本,就可以使用帐号限制。

如需限制 Google Play 中的帐号,请执行以下操作:

  1. 为 Google Play 软件包 com.android.vending 设置托管配置软件包。
  2. 在捆绑包中,将以英文逗号分隔的电子邮件地址作为 allowed_accounts 键的字符串值。

以下示例展示了如何限制帐号:

Kotlin

internal val ALLOWED_ACCOUNTS = "allowed_accounts"
internal val GOOGLE_PLAY_APK = "com.android.vending"

// ...

// Limit Google Play to one work and one personal account. Use
// a comma-separated list of account email addresses (usernames).
val googleAccounts = "ali@gmail.com,ali.connors@example.com"

// Supported in Google Play version 80970100 or higher.
val existingConfig = dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK)
val newConfig = Bundle(existingConfig)
newConfig.putString(ALLOWED_ACCOUNTS, googleAccounts)
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig)

Java

static final String ALLOWED_ACCOUNTS = "allowed_accounts";
static final String GOOGLE_PLAY_APK = "com.android.vending";

// ...


// Limit Google Play to one work and one personal account. Use
// a comma-separated list of account email addresses (usernames).
String googleAccounts = "ali@gmail.com,ali.connors@example.com";

// Supported in Google Play version 80970100 or higher.
Bundle existingConfig =
    dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK);
Bundle newConfig = new Bundle(existingConfig);
newConfig.putString(ALLOWED_ACCOUNTS, googleAccounts);
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig);

如需将 Google Play 限制为仅使用工作帐号,请在 DPC 知道帐号的电子邮件地址后,立即将 allowed_accounts 设置为单个受管理的帐号。空字符串会阻止用户在 Google Play 中使用任何帐号。

启用企业恢复出厂设置保护

使用企业恢复出厂设置保护,组织可以指定哪些 Google 帐号可以配置已恢复出厂设置的设备。

消费者恢复出厂设置保护旨在防止设备被盗。在允许任何人在未经授权的恢复出厂设置(例如通过 EMM)后配置设备之前,设置向导会要求用户针对之前在设备个人资料中的所有 Google 帐号进行身份验证。

在企业环境中,恢复出厂设置是在员工离职时管理员工设备的重要工具。但是,如果组织不知道员工的帐号凭据,则恢复出厂设置保护功能可能会阻止组织将设备发放给其他员工。

在恢复出厂设置后控制配置

在设备所有者模式下运行时,您的 DPC 可以使用 factoryResetProtectionAdmin 控制哪些帐号有权在恢复出厂设置后配置设备。如果此托管配置不存在、设为 null 或设为空列表,则获授权在恢复出厂设置后配置设备的帐号为设备个人资料中当前包含的帐号。

DPC 可以在完全受管设备的整个生命周期内配置这些帐号。

  1. DPC 会(手动或以编程方式)获取可在恢复出厂设置后配置设备的帐号 ID。
  2. DPC 使用特殊值“me”作为 userId 来指示经过身份验证的用户。ID 以整数字符串形式返回。新创建的帐号在 72 小时内可能无法用于恢复出厂设置。
  3. DPC 使用 DevicePolicyManager.setApplicationRestrictions() 设置适当的应用限制,以设置 settings 软件包中的键值对的值并指明这些限制适用的软件包:
    键名称 - factoryResetProtectionAdmin
    键值对 - 一个字符串,包含一个可配置恢复出厂设置设备的帐号 ID,或包含多个帐号 ID 的字符串数组。
    软件包名称 - com.google.android.gms.
  4. DPC 通过发送广播 com.google.android.gms.auth.FRP_CONFIG_CHANGED 启用可在恢复出厂设置后配置设备的帐号。

停用恢复出厂设置保护

若要停用恢复出厂设置保护,DPC 必须将 disableFactoryResetProtectionAdmin 的键值对设为 true。如果未设置此托管配置,则不会停用恢复出厂设置保护。

  1. DPC 使用 DevicePolicyManager.setApplicationRestrictions() 设置适当的应用限制,以设置 settings 软件包中键值对的值并指明限制所适用的软件包:
    键名称 - disableFactoryResetProtectionAdmin
    键值对 - truefalse(默认值)的布尔值。
    软件包名称 - com.google.android.gms.
  2. DPC 通过发送广播 com.google.android.gms.auth.FRP_CONFIG_CHANGED 向 Google Play 服务告知这一变更。

监控企业进程日志和远程错误报告

在 EMM 控制台中,管理员可以使用企业进程日志和远程 bug 报告监控全代管式设备。

记录企业设备活动

在设备所有者模式下运行的 DPC 可以通过远程跟踪设备活动(包括应用启动、Android 调试桥 (adb) activity 和屏幕解锁)来识别可疑活动。流程日志不需要用户同意。

如需启用或停用日志记录,DPC 会调用 setSecurityLoggingEnabled()

当有新一批日志可用时,DeviceAdminReceiver 会收到 onSecurityLogsAvailable() 回调。如需检索日志(收到回调后),DPC 会调用 retrieveSecurityLogs()

DPC 还可以调用 retrievePreRebootSecurityLogs() 来获取在上一个重新启动周期中生成的安全日志。这是设备上次重新启动和上次重新启动之间的时间间隔。不支持 retrieveSecurityLogs() 的设备返回 null。如果您的应用同时使用 retrievePreRebootSecurityLogs()retrieveSecurityLogs() 检索日志,您需要检查是否存在重复条目。

此设置会记录以下类型的操作,因此在安全事件后审核中非常有用:

  • 每次应用刚刚启动时。这有助于识别是否存在以被侵应用为首的恶意软件。
  • 设备尝试解锁失败的次数。这可以识别短时间内是否多次尝试解锁失败。
  • 当用户通过 USB 线将设备连接到计算机时,可能有害的 adb 命令。

如需详细了解如何读取日志,请参阅 SecurityLog

在开发和测试期间,您可以强制系统向 DPC 提供任何现有安全日志,而不必等待整批日志。在 Android 9.0(API 级别 28)或更高版本中,在终端中运行以下 Android 调试桥 (adb) 命令:

adb shell dpm force-security-logs

系统会限制您使用该工具的频率,并报告终端输出中的任何故意减慢速度。如果有可用的日志,您的 DPC 会收到 onSecurityLogsAvailable() 回调。

远程索取错误报告

在设备所有者模式下运行的 DPC 可以针对只有一个用户或关联用户的用户设备远程请求 bug 报告。bug 报告会在请求 bug 报告那一刻捕获设备活动,但也可能包含过去几小时内的活动,具体取决于 Logcat 缓冲区刷新的频率。

如需远程请求 bug 报告,DPC 会调用 requestBugreport()

授予和移除对客户端证书的访问权限

如果在资料所有者或设备所有者模式下运行的 DPC 授予第三方应用管理证书的权限,则该应用可以授予自己对其安装的证书的访问权限,而无需用户干预。如需安装配置文件中的所有应用都可以访问的证书,请使用 installKeyPair()

如需了解要配置哪些参数,请参阅 installKeyPair()。 此功能可与现有 API 结合使用,以管理证书。

部署场景

不使用 installKeyPair() 方法时:

  • 用户每次想要授予对证书的访问权限时,都需要点按证书的名称,然后点按允许
  • 用户在安装证书时会看到提示,并且必须为证书命名。

使用 installKeyPair() 方法:

  • 用户无需在每次想要授予对证书的访问权限时点按允许
  • 用户无法重命名证书。
  • 管理员拥有更大的控制权,因为他们可以为本不应访问特定证书的应用屏蔽证书。

移除客户端证书

在授予对客户端证书的访问权限后,如需远程移除通过 installKeyPair() 安装的客户端证书,请调用 removeKeyPair()

在设备所有者模式或资料所有者模式下运行的 DPC 或委托证书安装程序可以调用 removeKeyPair()。这会移除给定私钥别名下安装的证书和私钥对。

部署场景

如果组织正在迁移到更安全的客户端证书形式,请使用此功能。如果管理员发布新证书,并且发布需要很长时间,则管理员可以在迁移完成后撤消已弃用的证书。

安全密码已重置

您的 DPC 可以使用预注册的安全令牌授权更改,以重置用户密码。设备所有者和资料所有者可以调用安全密码重置 API 来分别更改设备和工作资料的密码。安全密码重置功能取代了 resetPassword(),并进行了以下改进:

如果您的 DPC build 以 Android 8.0(API 级别 26)或更高版本为目标平台,您应使用安全密码重置。在以 Android 8.0 或更高版本为目标平台的 DPC 中,调用 resetPassword() 会抛出 SecurityException,因此您可能需要更新 DPC。

设置和激活令牌

您的 DPC 需要先设置并激活令牌,然后才能重置密码。由于您的 DPC 可能无法立即使用令牌,因此您可以在 IT 管理员可能需要使用令牌的时间之前设置令牌。

密码重置令牌是一个强加密随机值,长度至少为 32 个字节。为每个设备和配置文件创建令牌,不要重复使用或共享生成的令牌。

我们建议将令牌(或对加密令牌的解密方式)存储在服务器上。如果您将令牌本地存储在凭据加密存储空间中,则在用户解锁设备或个人资料之前,DPC 无法重置密码。如果您将令牌本地存储在设备加密存储空间中,而该存储空间会遭到破解,攻击者可能会使用该令牌获取对工作资料或主要用户的访问权限。

您可以在 DPC 中生成新的令牌,也可以从服务器提取令牌。以下示例展示了 DPC 自行生成令牌并将其报告给服务器:

Kotlin

val token = ByteArray(32)

// Generate a new token
val random = SecureRandom()
random.nextBytes(token)

// Set the token to use at a later date
val success: Boolean
success = dpm.setResetPasswordToken(DeviceAdminReceiver.getComponentName(context), token)

// Activate the token and update success variable...

// Store the token on a server
if (success) {
    sendTokenToServer(token)
}

Java

byte token[] = new byte[32]; // Minimum size token accepted

// Generate a new token
SecureRandom random = new SecureRandom();
random.nextBytes(token);

// Set the token to use at a later date
boolean success;
success = dpm.setResetPasswordToken(DeviceAdminReceiver.getComponentName(getContext()), token);

// Activate the token and update success variable ...

// Store the token on a server
if (success) {
  sendTokenToServer(token);
}

在大多数情况下,设备政策控制器 (DPC) 设置令牌后需要将其激活。但是,如果用户没有锁定屏幕密码,系统会直接激活令牌。如需激活令牌,请让用户确认其凭据。您的 DPC 可以调用 KeyguardManager 方法 createConfirmDeviceCredentialIntent() 来获取用于启动确认的 Intent。在界面中向设备用户说明您为何要让他们进行身份验证。以下代码段展示了如何在 DPC 中激活令牌:

Kotlin

// In your DPC, you'll need to localize the user prompt
val ACTIVATE_TOKEN_PROMPT = "Use your credentials to enable remote password reset"
val ACTIVATE_TOKEN_REQUEST = 1

// Create or fetch a token and set it in setResetPasswordToken() ...
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
val confirmIntent = keyguardManager.createConfirmDeviceCredentialIntent(null, ACTIVATE_TOKEN_PROMPT)

if (confirmIntent != null) {
    startActivityForResult(confirmIntent, ACTIVATE_TOKEN_REQUEST)
    // Check your onActivityResult() callback for RESULT_OK
} else {
    // Null means the user doesn't have a lock screen so the token is already active.
    // Call isResetPasswordTokenActive() if you need to confirm
}

Java

// In your DPC, you'll need to localize the user prompt
static final String ACTIVATE_TOKEN_PROMPT =
    "Use your credentials to enable remote password reset";
static final int ACTIVATE_TOKEN_REQUEST = 1;

// Create or fetch a token and set it in setResetPasswordToken() ...

KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
Intent confirmIntent = keyguardManager.createConfirmDeviceCredentialIntent(
      null, ACTIVATE_TOKEN_PROMPT);

if (confirmIntent != null) {
  startActivityForResult(confirmIntent, ACTIVATE_TOKEN_REQUEST);
  // Check your onActivityResult() callback for RESULT_OK
} else {
  // Null means the user doesn't have a lock screen so the token is already active.
  // Call isResetPasswordTokenActive() if you need to confirm
}

您需要在设备重新启动之前激活 DPC 设置的令牌。Android 会将未激活的令牌存储在内存中,并且在重新启动后不会保留该令牌。如果用户在激活令牌之前重新启动设备,您的 DPC 可以重新设置相同的令牌或生成新的令牌。

您的 DPC 可以通过调用 isResetPasswordTokenActive() 并检查结果是否为 true 来确认令牌是否有效。

在 DPC 设置并激活令牌后,该令牌在 DPC 删除或替换令牌(或设备恢复出厂设置)之前将一直有效。该令牌独立于密码,不受用户更改或清除密码的影响。

删除令牌

您可以调用 clearResetPasswordToken() 来删除 DPC 之前设置的令牌。您可能需要撤消已遭破解的令牌,或撤消重置密码的功能。以下示例展示了如何在 DPC 中执行此操作:

Kotlin

val dpm = getDpm()
val admin = DeviceAdminReceiver.getComponentName(requireActivity())

// Clear the token
if (!dpm.clearResetPasswordToken(admin)) {
  // Report the failure and possibly try later ...
}

Java

DevicePolicyManager dpm = getDpm();
ComponentName admin = DeviceAdminReceiver.getComponentName(getActivity());

// Clear the token
if (!dpm.clearResetPasswordToken(admin)) {
  // Report the failure and possibly try later ...
}

重置密码

当 IT 管理员需要重置密码时,请调用 resetPasswordWithToken() 并传递 DPC 设置并提前激活的令牌:

Kotlin

val token: ByteArray = getTokenFromServer()
val newPassword = "password"

try {
  val result: Boolean = dpm.resetPasswordWithToken(
    DeviceAdminReceiver.getComponentName(requireContext()),
    newPassword,
    token,
    0
  )

  if (result) {
    // The password is now 'password'
  } else {
    // Using 'password' doesn't meet password restrictions
  }
} catch (e: IllegalStateException) {
  // The token doesn't match the one set earlier.
}

Java

byte token[] = getTokenFromServer();
String newPassword = "password";

try {
  boolean result = dpm.resetPasswordWithToken(
      DeviceAdminReceiver.getComponentName(getContext()), newPassword, token, 0);

  if (result) {
    // The password is now 'password'
  } else {
    // Using `password` doesn't meet password restrictions
  }
} catch (IllegalStateException e) {
  // The token doesn't match the one set earlier.
}

如果新密码不符合以下限制条件,则调用 resetPasswordWithToken() 会返回 false,并且密码不会更改:

  • 字符数满足任何最小密码长度限制。调用 getPasswordMinimumLength(),了解 IT 管理员是否设置了长度限制。
  • 密码中字符的范围和复杂程度符合组合限制条件。调用 getPasswordQuality(),了解 IT 管理员是否设置了组合限制条件。

如果密码质量限制条件不要求设置密码,您可以向 resetPasswordWithToken() 传递 null 或空字符串来移除密码。

工作资料安全性挑战

在资料所有者模式下运行的 DPC 可以要求用户为工作资料中运行的应用指定安全性质询。当用户尝试打开任何工作应用时,系统会显示安全质询。如果用户成功完成安全验证,系统会解锁工作资料并将其解密(如有必要)。

工作资料安全验证的运作方式

  1. 如果 DPC 发送 ACTION_SET_NEW_PASSWORD intent,系统会提示用户设置安全性验证。
  2. DPC 还可以发送 ACTION_SET_NEW_PARENT_PROFILE_PASSWORD intent 来提示用户设置设备锁定。

DPC 可为工作挑战设置密码政策,与其他设备密码政策不同。例如,设备质询响应的最小长度可以与其他密码所需的长度不同。DPC 使用常规的 DevicePolicyManager 方法(例如 setPasswordQuality()setPasswordMinimumLength())设置质询政策。

注意事项

  • DPC 可以重置工作资料的密码,但无法重置设备(个人)密码。如果用户选择将工作密码和个人密码设置为相同的密码,则对工作资料应用 resetPassword() 就会导致仅在工作资料上重置密码,而且密码与设备锁定屏幕的密码不同。
  • DPC 可以使用 setOrganizationColor()setOrganizationName() 自定义工作挑战的凭据屏幕。
  • 设备管理员无法使用 resetPassword() 来清除或更改已设置的密码。设备管理员仍可以设置密码,但只能在设备没有密码、PIN 码或图案时这样做。

如需了解详情,请参阅 getParentProfileInstance() 以及 DevicePolicyManager 下的参考文档。