セキュリティ

このガイドの機能では、Device Policy Controller(DPC)アプリに実装できるセキュリティ管理機能について説明します。このドキュメントにはコードサンプルが含まれています。Android のエンタープライズ機能のサンプルコードのソースとして Test DPC アプリを使用することもできます。

DPC アプリは、個人用デバイスではプロファイル所有者モードで、完全管理対象デバイスではデバイス所有者モードで実行できます。次の表は、DPC がプロファイル所有者モードまたはデバイス所有者モードで実行されている場合に使用できる機能を示しています。

機能 プロファイル所有者 デバイスの所有者
アプリへのアクセスを無効にする
提供元不明のアプリをブロックする
Google Play でアカウントを制限する
企業の出荷時設定へのリセット保護機能を有効にする
企業のプロセスログとリモート バグレポートをモニタリングする
クライアント証明書へのアクセス権を付与し、アクセス権を削除する
安全なパスコードのリセット
仕事用プロファイルのセキュリティ チャレンジ

アプリへのアクセスを無効にする

従業員が Android 搭載デバイスでゲームをプレイしたり YouTube を視聴したりすることを特定の時間帯または特定の曜日にブロックする必要がある場合は、DPC を使用してアプリへのアクセスを一時的に無効にできます。

アプリへのアクセスを無効にするには、デバイス所有者モードまたはプロファイル所有者モードで実行されている DPC が setPackagesSuspended() を構成します。これにより、選択したアプリが無効になっているかのように動作します(Google ランチャーによりアプリがグレー表示されます)。ユーザーがアプリをタップすると、アプリが一時停止されたことを示すシステム ダイアログが表示されます。

アプリが一時停止されている間は、アクティビティを開始できず、パッケージへの通知は抑制されます。一時停止されたパッケージは概要画面に表示されず、ダイアログ(トーストやスナックバーを含む)を表示できず、音声の再生やデバイスのバイブレーションもできません。

ランチャーは、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. バンドルに、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 Debug Bridge(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 は setFactoryResetProtectionPolicy() を使用して、初期状態へのリセット後にデバイスのプロビジョニングを許可するアカウントを制御できます。この構成が null または空のリストに設定されている場合、出荷時設定へのリセット後にデバイスのプロビジョニングが許可されるアカウントは、デバイスの個人用プロファイルのアカウントになります。

DPC は、完全管理対象デバイスの全期間を通じてこれらのアカウントを設定できます。

  1. IT 管理者は、特別な値 me を指定して People API の people.get メソッドを使用できます。これにより、ログインしているアカウントの userId が取得されます。userID は、people/[userId] の形式で整数文字列として resourceName キーで返されます。新たに作成されたアカウントは、72 時間は出荷時の設定にリセットできない可能性があります。
  2. また、出荷時設定へのリセット後に、1 人以上の IT 管理者がデバイスのロックを解除できるようにすることもできます。これらの IT 管理者に Google アカウントにログインし、ステップ 1 に沿って userId を共有してもらいます。これにより、次のステップでリストに userIds を追加できるようになります。
  3. DPC は、setFactoryResetProtectionPolicy() を使用して適切なアプリ制限を設定し、初期状態にリセットしたデバイスをプロビジョニングできる userId のリストを設定します。
  4. DPC は、ブロードキャスト com.google.android.gms.auth.FRP_CONFIG_CHANGED を明示的なインテントとして送信することで、出荷時設定へのリセット後にデバイスをプロビジョニングできるアカウントを有効にし、バックグラウンドの制限によるデバイスのドロップを防ぎます。

Kotlin

const val ACTION_FRP_CONFIG_CHANGED =
    "com.google.android.gms.auth.FRP_CONFIG_CHANGED"
const val GMSCORE_PACKAGE = "com.google.android.gms"

// ...

// List of userId that can provision a factory reset device.
// You can use the value returned calling people/me endpoint.
val accountIds = listOf("000000000000000000000")

dpm.setFactoryResetProtectionPolicy(
    adminName,
    FactoryResetProtectionPolicy.Builder()
        .setFactoryResetProtectionAccounts(accountIds)
        .setFactoryResetProtectionEnabled(true)
        .build()
)

val frpChangedIntent = Intent(ACTION_FRP_CONFIG_CHANGED)

frpChangedIntent.setPackage(GMSCORE_PACKAGE)
context.sendBroadcast(frpChangedIntent)

Java

static final String ACTION_FRP_CONFIG_CHANGED =
    "com.google.android.gms.auth.FRP_CONFIG_CHANGED";
static final String GMSCORE_PACKAGE = "com.google.android.gms";

// ...

// List of userId that can provision a factory reset device.
// You can use the value returned calling people/me endpoint.
List<String> accountIds = new ArrayList<String>();
accountIds.add("000000000000000000000");

dpm.setFactoryResetProtectionPolicy(
    adminName,
    new FactoryResetProtectionPolicy.Builder()
        .setFactoryResetProtectionAccounts(accountIds)
        .setFactoryResetProtectionEnabled(true)
        .build());

Intent frpChangedIntent = new Intent(ACTION_FRP_CONFIG_CHANGED);

frpChangedIntent.setPackage(GMSCORE_PACKAGE);
context.sendBroadcast(frpChangedIntent);

Legacy

API レベル 30 で導入された setFactoryResetProtectionPolicy() を使用できないデバイスの場合、DPC は setApplicationRestrictions を使用して、選択したアカウントを com.google.android.gms パッケージの factoryResetProtectionAdmin 管理対象設定に追加できます。

Kotlin

const val GOOGLE_PLAY_APK = "com.android.vending"
const val FACTORY_RESET_PROTECTION_ADMIN = "factoryResetProtectionAdmin"
const val DISABLE_FACTORY_RESET_PROTECTION_ADMIN = "disableFactoryResetProtectionAdmin"
const val GMSCORE_PACKAGE = "com.google.android.gms"

// ...

val existingConfig = dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK)
val newConfig = Bundle(existingConfig)
newConfig.putBoolean(DISABLE_FACTORY_RESET_PROTECTION_ADMIN, false)
newConfig.putString(FACTORY_RESET_PROTECTION_ADMIN, googleAccounts)
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig)

val frpChangedIntent = Intent(ACTION_FRP_CONFIG_CHANGED)

frpChangedIntent.setPackage(GMSCORE_PACKAGE)
context.sendBroadcast(frpChangedIntent)

Java

static final String GOOGLE_PLAY_APK = "com.android.vending";
static final String FACTORY_RESET_PROTECTION_ADMIN = "factoryResetProtectionAdmin";
static final String DISABLE_FACTORY_RESET_PROTECTION_ADMIN = "disableFactoryResetProtectionAdmin";
static final String GMSCORE_PACKAGE = "com.google.android.gms";

// ...

Bundle existingConfig =
        dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK);
Bundle newConfig = new Bundle(existingConfig);
newConfig.putBoolean(DISABLE_FACTORY_RESET_PROTECTION_ADMIN, false);
newConfig.putStringArray(FACTORY_RESET_PROTECTION_ADMIN,
        accountIds.toArray(new String[accountIds.size()]));
dpm.setApplicationRestrictions(adminName, GOOGLE_PLAY_APK, newConfig);

Intent frpChangedIntent = new Intent(ACTION_FRP_CONFIG_CHANGED);

frpChangedIntent.setPackage(GMSCORE_PACKAGE);
context.sendBroadcast(frpChangedIntent);

企業の出荷時設定へのリセット保護機能を無効にする

出荷時設定へのリセット保護機能を無効にするには、DPC で setFactoryResetProtectionPolicy() を使用して値 null を渡します。

Kotlin

const val ACTION_FRP_CONFIG_CHANGED =
    "com.google.android.gms.auth.FRP_CONFIG_CHANGED"
const val GMSCORE_PACKAGE = "com.google.android.gms"

// ...

dpm.setFactoryResetProtectionPolicy(adminName, null)

val frpChangedIntent = Intent(ACTION_FRP_CONFIG_CHANGED)

frpChangedIntent.setPackage(GMSCORE_PACKAGE)
context.sendBroadcast(frpChangedIntent)

Java

static final String ACTION_FRP_CONFIG_CHANGED =
    "com.google.android.gms.auth.FRP_CONFIG_CHANGED";
static final String GMSCORE_PACKAGE = "com.google.android.gms";

// ...

dpm.setFactoryResetProtectionPolicy(adminName, null);

Intent frpChangedIntent = new Intent(ACTION_FRP_CONFIG_CHANGED);

frpChangedIntent.setPackage(GMSCORE_PACKAGE);
context.sendBroadcast(frpChangedIntent);

Legacy

API レベル 30 で導入された setFactoryResetProtectionPolicy() を使用できないデバイスの場合、DPC は setApplicationRestrictions を使用して、com.google.android.gms パッケージの disableFactoryResetProtectionAdmin マネージド構成に Key-Value として true を設定できます。

Kotlin

const val GOOGLE_PLAY_APK = "com.android.vending"
const val FACTORY_RESET_PROTECTION_ADMIN = "factoryResetProtectionAdmin"
const val DISABLE_FACTORY_RESET_PROTECTION_ADMIN = "disableFactoryResetProtectionAdmin"
const val GMSCORE_PACKAGE = "com.google.android.gms"

// ...

val existingConfig = dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK)
val newConfig = Bundle(existingConfig)
newConfig.putBoolean(DISABLE_FACTORY_RESET_PROTECTION_ADMIN, true)

dpm.setApplicationRestrictions(
    adminName, GOOGLE_PLAY_SERVICES_PACKAGE, restrictions
)

val frpChangedIntent = Intent(ACTION_FRP_CONFIG_CHANGED)

frpChangedIntent.setPackage(GMSCORE_PACKAGE)
context.sendBroadcast(frpChangedIntent)

Java

static final String GOOGLE_PLAY_APK = "com.android.vending";
static final String FACTORY_RESET_PROTECTION_ADMIN = "factoryResetProtectionAdmin";
static final String DISABLE_FACTORY_RESET_PROTECTION_ADMIN = "disableFactoryResetProtectionAdmin";
static final String GMSCORE_PACKAGE = "com.google.android.gms";

// ...

Bundle existingConfig =
        dpm.getApplicationRestrictions(adminName, GOOGLE_PLAY_APK);
Bundle newConfig = new Bundle(existingConfig);
newConfig.putBoolean(DISABLE_FACTORY_RESET_PROTECTION_ADMIN, true);

dpm.setApplicationRestrictions(
    adminName, GOOGLE_PLAY_SERVICES_PACKAGE, restrictions);

Intent frpChangedIntent = new Intent(ACTION_FRP_CONFIG_CHANGED);

frpChangedIntent.setPackage(GMSCORE_PACKAGE);
context.sendBroadcast(frpChangedIntent);

企業のプロセスログとリモートバグレポートを監視する

EMM コンソールでは、管理者はエンタープライズ プロセスログとリモート バグレポートを使用して、完全管理対象デバイスをモニタリングできます。

企業デバイスのアクティビティを記録する

デバイス所有者モードで実行されている DPC は、アプリの起動、Android Debug Bridge(adb)アクティビティ、画面のロック解除などのデバイスのアクティビティをリモートで追跡することで、不審なアクティビティを特定できます。プロセスログはユーザーの同意を必要としない。

ロギングを有効または無効にするために、DPC は setSecurityLoggingEnabled() を呼び出します。

ログの新しいバッチが利用可能になると、DeviceAdminReceiveronSecurityLogsAvailable() コールバックを受け取ります。コールバックの受信後にログを取得するために、DPC は retrieveSecurityLogs() を呼び出します。

DPC は retrievePreRebootSecurityLogs() を呼び出して、前の再起動サイクルで生成されたセキュリティ ログを取得することもできます。これは、最後のデバイスの再起動から前回の再起動までの間隔です。retrieveSecurityLogs() をサポートしていないデバイスは null を返します。アプリが retrievePreRebootSecurityLogs()retrieveSecurityLogs() の両方を使用してログを取得する場合は、エントリの重複を確認する必要があります。
注: この機能は、デバイスに 1 人のユーザーまたは関連付けられたユーザーが存在する完全管理対象デバイス上のアクティビティのみを記録します。この機能はデバイス全体のアクティビティを記録するため、個人用デバイスでは動作しません。

この設定は、次の種類のアクションがログに記録されるため、セキュリティ イベント後の監査に役立ちます。

  • アプリが新たに起動されるたび。これは、侵害されたアプリを起点とするマルウェアを特定するのに役立ちます。
  • デバイスでロック解除に失敗した回数。これにより、ロック解除の試行が短期間に数回失敗しているかどうかを識別できます。
  • ユーザーが USB ケーブルを使用してデバイスをパソコンに接続したときの、有害な可能性がある adb コマンド。

ログの読み方については、SecurityLog をご覧ください。

開発とテストの際に、既存のセキュリティ ログを DPC で利用できるようにシステムに強制できます。完全なバッチを待つ必要はありません。Android 9.0(API レベル 28)以降では、ターミナルで次の Android Debug Bridge(adb)コマンドを実行します。

adb shell dpm force-security-logs

システムはツールの使用頻度を制限し、意図的な速度低下をターミナル出力に報告します。利用可能なログがある場合、DPC は onSecurityLogsAvailable() コールバックを受信します。

バグレポートをリモートでリクエストする

デバイス所有者モードで実行されている DPC は、1 人のユーザーまたは関連ユーザーのみがいるユーザー デバイスのバグレポートをリモートでリクエストできます。バグレポートは、バグレポートがリクエストされた時点のデバイスのアクティビティをキャプチャしますが、logcat バッファの更新頻度によっては、過去数時間のアクティビティが含まれる場合もあります。

バグレポートをリモートでリクエストする場合、DPC は requestBugreport() を呼び出します。

クライアント証明書へのアクセス権を付与し、アクセス権を削除する

プロファイル オーナー モードまたはデバイス オーナー モードで実行されている DPC が、サードパーティ アプリに証明書を管理する権限を付与した場合、アプリはユーザーの介入なしに、インストールする証明書へのアクセス権をアプリ自体に付与できます。プロファイル内のすべてのアプリがアクセスできる証明書をインストールするには、installKeyPair() を使用します。

設定するパラメータについては、installKeyPair() をご覧ください。この機能は、証明書を管理するための既存の API と連携して動作します。

デプロイ シナリオ

installKeyPair() メソッドを使用しない場合:

  • ユーザーは証明書へのアクセス権を付与するたびに、証明書の名前をタップして [許可] をタップする必要があります。
  • 証明書のインストール時にプロンプトが表示され、証明書に名前を付ける必要があります。

installKeyPair() メソッドの場合:

  • ユーザーは、証明書へのアクセスを許可するたびに [許可] をタップする必要はありません。
  • ユーザーは証明書の名前を変更できません。
  • 管理者は、特定の証明書にアクセスできないアプリの証明書をブロックすることで、より詳細に制御できます。

クライアント証明書を削除する

クライアント証明書へのアクセス権を付与した後、installKeyPair() を介してインストールされたクライアント証明書をリモートで削除するには、removeKeyPair() を呼び出します。

デバイス所有者モードまたはプロファイル所有者モードで実行されている DPC、または委任証明書インストーラは、removeKeyPair() を呼び出すことができます。これにより、特定の秘密鍵エイリアスにインストールされている証明書と秘密鍵のペアが削除されます。

デプロイ シナリオ

この機能は、組織がより安全な形式のクライアント証明書に移行する場合に使用します。管理者が新しい証明書をロールアウトし、その配布にかなりの時間がかかる場合、管理者は移行の完了後に非推奨の証明書を取り消すことができます。

安全なパスコードのリセット

DPC は、事前登録された安全なトークンを使用して変更を承認することで、ユーザーのパスワードを再設定できます。デバイス オーナーとプロファイル オーナーは、安全なパスコード リセット API を呼び出して、デバイスと仕事用プロファイルのパスワードを変更できます。安全なパスコードのリセットにより、resetPassword() が次のように改善されます。

  • DPC は、ファイルベースの暗号化を使用して、ユーザーがデバイスを再起動した後、デバイスまたはプロファイルのロックを解除する前にパスコードをリセットできます。
  • Android Keystore は、パスコードのリセット後もユーザー認証鍵を保持します。

DPC ビルドが Android 8.0(API レベル 26)以降をターゲットとしている場合は、セキュア パスコードのリセットを使用する必要があります。resetPassword() を呼び出すと、Android 8.0 以降をターゲットとする DPC で 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 管理者が作成の制約を設定しているかどうかを確認します。

パスワード品質の制約によりパスワードの設定が不要な場合は、null または空の文字列を resetPasswordWithToken() に渡してパスワードを削除できます。

仕事用プロファイルのセキュリティ チャレンジ

プロファイル所有者モードで実行される DPC では、仕事用プロファイルで実行されているアプリに対するセキュリティ チャレンジの指定を必須にできます。ユーザーが仕事用アプリを開こうとすると、セキュリティ チャレンジが表示されます。ユーザーがセキュリティ チャレンジに成功すると、仕事用プロファイルのロックが解除され、必要に応じて復号されます。

仕事用プロファイルのセキュリティ チャレンジの仕組み

  1. DPC が ACTION_SET_NEW_PASSWORD インテントを送信すると、システムはユーザーにセキュリティ チャレンジを設定するよう求めます。
  2. また、DPC は ACTION_SET_NEW_PARENT_PROFILE_PASSWORD インテントを送信して、ユーザーにデバイスのロックを設定するよう促すこともできます。

DPC では、仕事用チャレンジ用のパスワード ポリシーを、他のデバイス パスワード用のポリシーとは異なる方法で設定できます。たとえば、デバイス チャレンジ レスポンスの最小長は、他のパスワードに必要な長さと異なる場合があります。DPC は、setPasswordQuality()setPasswordMinimumLength() などの通常の DevicePolicyManager メソッドを使用してチャレンジ ポリシーを設定します。

考慮事項

  • DPC は仕事用プロファイルのパスワードを再設定できますが、デバイス(個人用)のパスワードを再設定することはできません。ユーザーが仕事用パスワードと個人用パスワードを同じに設定することを選択した場合、仕事用プロファイルの resetPassword() により、パスワードは仕事用プロファイルでのみリセットされ、デバイスのロック画面用のパスワードと同じにはなりません。
  • DPC は、setOrganizationColor()setOrganizationName() を使用して、仕事用チャレンジの認証情報画面をカスタマイズできます。
  • デバイス管理者は、resetPassword() を使用してパスワードをクリアしたり、設定済みのパスワードを変更したりすることはできません。デバイス管理者は、デバイスにパスワード、PIN、パターンが設定されていない場合にのみパスワードを設定できます。

詳細については、getParentProfileInstance() と、DevicePolicyManager のリファレンス ドキュメントをご覧ください。