アプリのセキュリティに関するおすすめの方法

アプリをセキュアにすることで、ユーザーの信頼とデバイスの整合性を保つことができます。

このページでは、アプリのセキュリティに大きなプラスの影響を与えるベスト プラクティスについて説明します。

通信をセキュアにする

アプリと他のアプリ間や、アプリとウェブサイト間で交換されるデータを保護すると、アプリの安定性が向上し、送受信するデータが保護されます。

暗黙的インテントとエクスポートされていないコンテンツ プロバイダを使用する

アプリチューザを表示する

暗黙的インテントがユーザーのデバイス上で少なくとも 2 つのアプリ候補を起動できる場合、アプリチューザを明示的に表示します。このインタラクション方法により、ユーザーは、信頼するアプリに機密情報を送信できるようなります。

Kotlin

    val intent = Intent(ACTION_SEND)
    val possibleActivitiesList: List<ResolveInfo> =
            queryIntentActivities(intent, PackageManager.MATCH_ALL)

    // Verify that an activity in at least two apps on the user's device
    // can handle the intent. Otherwise, start the intent only if an app
    // on the user's device can handle the intent.
    if (possibleActivitiesList.size > 1) {

        // Create intent to show chooser.
        // Title is something similar to "Share this photo with".

        val chooser = resources.getString(R.string.chooser_title).let { title ->
            Intent.createChooser(intent, title)
        }
        startActivity(chooser)
    } else if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
    

Java

    Intent intent = new Intent(Intent.ACTION_SEND);
    List<ResolveInfo> possibleActivitiesList =
            queryIntentActivities(intent, PackageManager.MATCH_ALL);

    // Verify that an activity in at least two apps on the user's device
    // can handle the intent. Otherwise, start the intent only if an app
    // on the user's device can handle the intent.
    if (possibleActivitiesList.size() > 1) {

        // Create intent to show chooser.
        // Title is something similar to "Share this photo with".

        String title = getResources().getString(R.string.chooser_title);
        Intent chooser = Intent.createChooser(intent, title);
        startActivity(chooser);
    } else if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
    

関連情報:

署名ベースのパーミッションを適用する

制御や所有の対象となっている 2 つのアプリ間でデータを共有する場合は、署名ベースのパーミッションを使用してください。署名ベースのパーミッションの場合、ユーザーの確認は不要で、代わりに、データにアクセスするアプリが同じ署名鍵を使用して署名されているかチェックされます。署名ベースのパーミッションにより、効率的でセキュアなユーザー エクスペリエンスを実現できます。

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.myapp">
        <permission android:name="my_custom_permission_name"
                    android:protectionLevel="signature" />
    

関連情報:

アプリのコンテンツ プロバイダに対するアクセスを許可しない

自分のアプリから別の非所有アプリに向けてデータを送信する場合を除き、アプリに含まれる ContentProvider オブジェクトに対して他のデベロッパーのアプリがアクセスすることを明示的に禁止する必要があります。この設定は、Android 4.1.1(API レベル 16)以前を搭載しているデバイスにアプリをインストールできる場合に特に重要です。このような Android バージョンの場合、<provider> 要素の android:exported 属性がデフォルトで true になっています。

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.myapp">
        <application ... >
            <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="com.example.myapp.fileprovider"
                ...
                android:exported="false">
                <!-- Place child elements of <provider> here. -->
            </provider>
            ...
        </application>
    </manifest>
    

機密情報を表示する前に認証情報を要求する

ユーザーがアプリ内の機密情報やプレミアム コンテンツにアクセスできるよう、ユーザーに認証情報をリクエストする場合は、PIN / パスワード / パターンや生体認証情報(顔認証、指紋認識など)を要求するようにします。

生体認証情報をリクエストする方法については、生体認証ガイドをご覧ください。

ネットワーク セキュリティ対策を適用する

以下のセクションでは、アプリのネットワーク セキュリティを改善する方法について説明します。

SSL トラフィックを使用する

信頼できる有名な CA が発行した証明書を持つウェブサーバーとアプリが通信する場合、HTTPS リクエストは非常にシンプルです。

Kotlin

    val url = URL("https://www.google.com")
    val urlConnection = url.openConnection() as HttpsURLConnection
    urlConnection.connect()
    urlConnection.inputStream.use {
        ...
    }
    

Java

    URL url = new URL("https://www.google.com");
    HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
    urlConnection.connect();
    InputStream in = urlConnection.getInputStream();
    

ネットワーク セキュリティ構成を追加する

アプリが新しい CA やカスタム CA を使用する場合は、構成ファイル内でネットワークのセキュリティ設定を宣言します。このプロセスにより、アプリコードを編集することなく、ネットワーク セキュリティ構成を作成できます。

ネットワーク セキュリティ構成ファイルをアプリに追加する手順は次のとおりです。

  1. アプリのマニフェスト内で構成を宣言します。
  2.     <manifest ... >
            <application
                android:networkSecurityConfig="@xml/network_security_config"
                ... >
                <!-- Place child elements of <application> element here. -->
            </application>
        </manifest>
        
  3. res/xml/network_security_config.xml にある XML リソース ファイルを追加します。

    平文を無効にして、対象ドメインに対するすべてのトラフィックが HTTPS を使用するように指定します。

        <network-security-config>
            <domain-config cleartextTrafficPermitted="false">
                <domain includeSubdomains="true">secure.example.com</domain>
                ...
            </domain-config>
        </network-security-config>
        

    開発プロセス中に、<debug-overrides> 要素を使用することで、ユーザーがインストールした証明書を明示的に許可できます。この要素は、アプリのリリース構成に影響を与えることなく、デバッグ中やテスト中にアプリのセキュリティ クリティカル オプションをオーバーライドします。アプリのネットワーク セキュリティ構成 XML ファイル内でこの要素を定義する方法を以下のスニペットに示します。

        <network-security-config>
            <debug-overrides>
                <trust-anchors>
                    <certificates src="user" />
                </trust-anchors>
            </debug-overrides>
        </network-security-config>
        

関連情報: ネットワーク セキュリティ構成

独自のトラスト マネージャーを作成する

SSL チェッカーは、必ずしもすべての証明書を受け入れるべきではありません。次のいずれかの条件が当てはまるユースケースの場合は、トラスト マネージャーをセットアップして、発生するすべての SSL 警告を処理することをおすすめします。

  • 新しい CA やカスタム CA によって署名された証明書を持つウェブサーバーと通信する場合。
  • CA が、使用しているデバイスによって信頼されていない場合。
  • ネットワーク セキュリティ構成を使用できない場合。

各手順を完了する方法については、未知の認証局の処理に関する説明をご覧ください。

関連情報:

WebView オブジェクトを慎重に使用する

可能な限り、WebView オブジェクト内のホワイトリスト コンテンツだけを読み込むようにしてください。つまり、デベロッパーが制御できないサイトにユーザーが移動するのを、アプリ内の WebView オブジェクトが許可しないようにする必要があります。

また、アプリの WebView オブジェクト内のコンテンツを完全に制御し、信頼している場合を除いて、JavaScript インターフェース サポートを有効にしないでください。

HTML メッセージ チャネルを使用する

Android 6.0(API レベル 23)以降を搭載しているデバイス上で JavaScript インターフェース サポートを使用する必要があるアプリの場合は、次のコード スニペットに示すように、ウェブサイトとアプリ間の通信ではなく、HTML メッセージ チャネルを使用します。

Kotlin

    val myWebView: WebView = findViewById(R.id.webview)

    // messagePorts[0] and messagePorts[1] represent the two ports.
    // They are already tangled to each other and have been started.
    val channel: Array<out WebMessagePort> = myWebView.createWebMessageChannel()

    // Create handler for channel[0] to receive messages.
    channel[0].setWebMessageCallback(object : WebMessagePort.WebMessageCallback() {

        override fun onMessage(port: WebMessagePort, message: WebMessage) {
            Log.d(TAG, "On port $port, received this message: $message")
        }
    })

    // Send a message from channel[1] to channel[0].
    channel[1].postMessage(WebMessage("My secure message"))
    

Java

    WebView myWebView = (WebView) findViewById(R.id.webview);

    // messagePorts[0] and messagePorts[1] represent the two ports.
    // They are already tangled to each other and have been started.
    WebMessagePort[] channel = myWebView.createWebMessageChannel();

    // Create handler for channel[0] to receive messages.
    channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
        @Override
        public void onMessage(WebMessagePort port, WebMessage message) {
             Log.d(TAG, "On port " + port + ", received this message: " + message);
        }
    });

    // Send a message from channel[1] to channel[0].
    channel[1].postMessage(new WebMessage("My secure message"));
    

関連情報:

適切なパーミッションを付与する

アプリは、適切に機能するために必要となる最小限のパーミッションだけをリクエストするようにします。可能であれば、付与されたパーミッションの一部が不要になった場合、アプリはそのパーミッションを放棄するようにします。

インテントを使用してパーミッションを譲る

パーミッションをアプリに追加する際、別のアプリで実行できるアクションのために追加することはできるだけ避けてください。代わりに、インテントを使用して、すでに必要なパーミッションを持っている別のアプリにリクエストを譲ります。

READ_CONTACTS パーミッションや WRITE_CONTACTS パーミッションをリクエストするのではなく、インテントを使用して、ユーザーを連絡帳アプリに誘導する方法の例を以下に示します。

Kotlin

    // Delegates the responsibility of creating the contact to a contacts app,
    // which has already been granted the appropriate WRITE_CONTACTS permission.
    Intent(Intent.ACTION_INSERT).apply {
        type = ContactsContract.Contacts.CONTENT_TYPE
    }.also { intent ->
        // Make sure that the user has a contacts app installed on their device.
        intent.resolveActivity(packageManager)?.run {
            startActivity(intent)
        }
    }
    

Java

    // Delegates the responsibility of creating the contact to a contacts app,
    // which has already been granted the appropriate WRITE_CONTACTS permission.
    Intent insertContactIntent = new Intent(Intent.ACTION_INSERT);
    insertContactIntent.setType(ContactsContract.Contacts.CONTENT_TYPE);

    // Make sure that the user has a contacts app installed on their device.
    if (insertContactIntent.resolveActivity(getPackageManager()) != null) {
        startActivity(insertContactIntent);
    }
    

また、ストレージに対するアクセスやファイルの選択など、ファイルベース I/O を実行する必要があるアプリの場合、アプリに代わってシステムが処理を完了することができるため、アプリに特別なパーミッションは必要ありません。ユーザーが特定の URI でコンテンツを選択すると、選択されたリソースに対するパーミッションが呼び出し元のアプリに付与されます。

関連情報:

アプリ間でセキュアにデータを共有する

アプリのコンテンツをセキュアな方法で他のアプリと共有するには、以下のベスト プラクティスを適用してください。

  • 必要に応じて、読み取り専用または書き込み専用のパーミッションを適用します。
  • FLAG_GRANT_READ_URI_PERMISSION フラグや FLAG_GRANT_WRITE_URI_PERMISSION フラグを使用することで、データに対する 1 回限りのアクセス権限をクライアントに付与します。
  • データを共有する際は、「file://」URI ではなく「content://」URI を使用します。デベロッパーの代わりに、FileProvider のインスタンスがこの処理を行います。

URI パーミッション付与フラグとコンテンツ プロバイダ パーミッションを使用することで、アプリの PDF ファイルを別の PDF ビューアアプリ内で表示する方法を、次のコード スニペットに示します。

Kotlin

    // Create an Intent to launch a PDF viewer for a file owned by this app.
    Intent(Intent.ACTION_VIEW).apply {
        data = Uri.parse("content://com.example/personal-info.pdf")

        // This flag gives the started app read access to the file.
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }.also { intent ->
        // Make sure that the user has a PDF viewer app installed on their device.
        intent.resolveActivity(packageManager)?.run {
            startActivity(intent)
        }
    }
    

Java

    // Create an Intent to launch a PDF viewer for a file owned by this app.
    Intent viewPdfIntent = new Intent(Intent.ACTION_VIEW);
    viewPdfIntent.setData(Uri.parse("content://com.example/personal-info.pdf"));

    // This flag gives the started app read access to the file.
    viewPdfIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    // Make sure that the user has a PDF viewer app installed on their device.
    if (viewPdfIntent.resolveActivity(getPackageManager()) != null) {
        startActivity(viewPdfIntent);
    }
    

注: 信頼できないアプリが Android 10(API レベル 29)以降をターゲットとしている場合、そのアプリのホーム ディレクトリ内にあるファイルで exec() を呼び出すことはできません。書き込み可能なアプリのホーム ディレクトリからファイルをこのように実行することは、W^X 違反です。アプリはその APK ファイル内に埋め込まれたバイナリコードのみ読み込むようにする必要があります。また、Android 10 以降をターゲットとしているアプリの場合、dlopen() を使用して開いたファイルの実行コードをメモリ内で編集することはできません。たとえば、テキストの再配置を伴うすべての共有オブジェクト ファイル(.so)などが該当します。

関連情報: android:grantUriPermissions

データを安全に保存する

アプリがユーザーの機密情報に対するアクセスを必要とする場合がありますが、ユーザーは、データが適切に保護されると信頼できる場合に限り、機密データに対するアクセスをアプリに付与します。

内部ストレージに個人データを保存する

ユーザーの個人データはすべて、アプリごとにサンドボックス化されているデバイスの内部ストレージ内に保存します。このファイルを表示する際、アプリがパーミッションをリクエストする必要はなく、他のアプリはファイルにアクセスできません。また、追加のセキュリティ対策として、ユーザーがアプリをアンインストールすると、デバイスは、アプリが内部ストレージに保存したすべてのファイルを削除します。

注: 保存しているデータが特に機密の高いものまたは非公開である場合は、File オブジェクトではなく、セキュリティ ライブラリから利用できる EncryptedFile オブジェクトの使用をご検討ください。

ストレージにデータを書き込む方法の例を次のコード スニペットに示します。

Kotlin

    // Although you can define your own key generation parameter specification, it's
    // recommended that you use the value specified here.
    val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
    val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

    // Creates a file with this name, or replaces an existing file
    // that has the same name. Note that the file name cannot contain
    // path separators.
    val fileToWrite = "my_sensitive_data.txt"
    val encryptedFile = EncryptedFile.Builder(
        File(directory, fileToWrite),
        context,
        masterKeyAlias,
        EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
    ).build()

    encryptedFile.openFileOutput().bufferedWriter().use {
        it.write("MY SUPER-SECRET INFORMATION")
    }
    

Java

    // Although you can define your own key generation parameter specification, it's
    // recommended that you use the value specified here.
    KeyGenParameterSpec keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC;
    String masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec);

    // Creates a file with this name, or replaces an existing file
    // that has the same name. Note that the file name cannot contain
    // path separators.
    String fileToWrite = "my_sensitive_data.txt";
    try {
        EncryptedFile encryptedFile = new EncryptedFile.Builder(
                new File(directory, fileToWrite),
                context,
                masterKeyAlias,
                EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
        ).build();

        // Write to a file.
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
                encryptedFile.openFileOutput()));
        writer.write("MY SUPER-SECRET INFORMATION");
    } catch (GeneralSecurityException gse) {
        // Error occurred getting or creating keyset.
    } catch (IOException ex) {
        // Error occurred opening file for writing.
    }
    

ストレージからデータを読み取る逆向きの処理を次のコード スニペットに示します。

Kotlin

    // Although you can define your own key generation parameter specification, it's
    // recommended that you use the value specified here.
    val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
    val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

    val fileToRead = "my_sensitive_data.txt"
    val encryptedFile = EncryptedFile.Builder(
        File(directory, fileToRead),
        context,
        masterKeyAlias,
        EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
    ).build()

    val contents = encryptedFile.bufferedReader().useLines { lines ->
        lines.fold("") { working, line ->
            "$working\n$line"
        }
    }
    

Java

    // Although you can define your own key generation parameter specification, it's
    // recommended that you use the value specified here.
    KeyGenParameterSpec keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC;
    String masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec);

    String fileToRead = "my_sensitive_data.txt";
    EncryptedFile encryptedFile = new EncryptedFile.Builder(
            new File(directory, fileToRead),
            context,
            masterKeyAlias,
            EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
    ).build();

    StringBuffer stringBuffer = new StringBuffer();
    try (BufferedReader reader =
                 new BufferedReader(new FileReader(encryptedFile))) {

        String line = reader.readLine();
        while (line != null) {
            stringBuffer.append(line).append('\n');
            line = reader.readLine();
        }
    } catch (IOException e) {
        // Error occurred opening raw file for reading.
    } finally {
        String contents = stringBuffer.toString();
    }
    

関連情報:

ユースケースに基づいて外部ストレージにデータを保存する

アプリに固有で機密性の低いサイズの大きなファイルや、アプリが他のアプリと共有しているファイルには、外部ストレージを使用します。使用する API は、アプリが アプリ固有のファイルにアクセスするように設計されているか、共有ファイルにアクセスするように設計されているかによって、それぞれ異なります。

ストレージの空き容量を確認する

アプリがリムーバブル外部ストレージ デバイスとやり取りする場合、アプリがアクセスしようとしているときに、ユーザーがストレージ デバイスを取り外す可能性があることに注意が必要です。ストレージ デバイスが使用可能であることを確認するロジックを含めます。

アプリ固有のファイルにアクセスする

ファイルに個人情報や機密情報が含まれておらず、アプリ内でのみユーザーに利用される場合は、外部ストレージ上のアプリ固有のディレクトリにファイルを保存します。

共有ファイルにアクセスする

アプリで、他のアプリに利用されるファイルにアクセスまたは保存する必要がある場合は、ユースケースに応じて次のいずれかの API を使用します。

  • メディア ファイル: アプリ間で共有されている画像、音声ファイル、動画を保存し、アクセスするには、Media Store API を使用します。
  • その他のファイル: ダウンロードしたファイルを含む、その他の種類の共有ファイルを保存し、アクセスするには、ストレージ アクセス フレームワークを使用します。

データの有効性をチェックする

アプリが外部ストレージのデータを使用する場合、データの内容に破損や変更がないことを確認する必要があります。また、安定した形式ではなくなったファイルを処理するロジックもアプリに組み込む必要があります。

ハッシュ検証ツールの例を次のコード スニペットに示します。

Kotlin

    val hash = calculateHash(stream)
    // Store "expectedHash" in a secure location.
    if (hash == expectedHash) {
        // Work with the content.
    }

    // Calculating the hash code can take quite a bit of time, so it shouldn't
    // be done on the main thread.
    suspend fun calculateHash(stream: InputStream): String {
        return withContext(Dispatchers.IO) {
            val digest = MessageDigest.getInstance("SHA-512")
            val digestStream = DigestInputStream(stream, digest)
            while (digestStream.read() != -1) {
                // The DigestInputStream does the work; nothing for us to do.
            }
            digest.digest().joinToString(":") { "%02x".format(it) }
        }
    }
    

Java

    Executor threadPoolExecutor = Executors.newFixedThreadPool(4);
    private interface HashCallback {
        void onHashCalculated(@Nullable String hash);
    }

    boolean hashRunning = calculateHash(inputStream, threadPoolExecutor, hash -> {
        if (Objects.equals(hash, expectedHash)) {
            // Work with the content.
        }
    });

    if (!hashRunning) {
        // There was an error setting up the hash function.
    }

    private boolean calculateHash(@NonNull InputStream stream,
                                  @NonNull Executor executor,
                                  @NonNull HashCallback hashCallback) {
        final MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("SHA-512");
        } catch (NoSuchAlgorithmException nsa) {
            return false;
        }

        // Calculating the hash code can take quite a bit of time, so it shouldn't
        // be done on the main thread.
        executor.execute(() -> {
            String hash;
            try (DigestInputStream digestStream =
                    new DigestInputStream(stream, digest)) {
                while (digestStream.read() != -1) {
                    // The DigestInputStream does the work; nothing for us to do.
                }
                StringBuilder builder = new StringBuilder();
                for (byte aByte : digest.digest()) {
                    builder.append(String.format("%02x", aByte)).append(':');
                }
                hash = builder.substring(0, builder.length() - 1);
            } catch (IOException e) {
                hash = null;
            }

            final String calculatedHash = hash;
            runOnUiThread(() -> hashCallback.onHashCalculated(calculatedHash));
        });
        return true;
    }
    

キャッシュ ファイルに保存するのは機密性の低いデータだけに限定する

機密性の低いアプリデータに対する迅速なアクセスを実現するには、そのようなデータをデバイスのキャッシュ内に保存します。サイズが 1 MB を超えるキャッシュの場合は、getExternalCacheDir() を使用します。超えていない場合は、getCacheDir() を使用します。各メソッドは、アプリのキャッシュ データを格納する File オブジェクトを提供します。

アプリが最近ダウンロードしたファイルをキャッシュする方法を次のコード スニペットに示します。

Kotlin

    val cacheFile = File(myDownloadedFileUri).let { fileToCache ->
        File(cacheDir.path, fileToCache.name)
    }
    

Java

    File cacheDir = getCacheDir();
    File fileToCache = new File(myDownloadedFileUri);
    String fileToCacheName = fileToCache.getName();
    File cacheFile = new File(cacheDir.getPath(), fileToCacheName);
    

注: getExternalCacheDir() を使用して共有ストレージ内にアプリのキャッシュを配置した場合、アプリの実行中に、ユーザーがこのストレージを含むメディアを取り外す可能性がある点に注意してください。このユーザー行動が引き起こすキャッシュミスを適切に処理するロジックを組み込んでおく必要があります。

注: このようなファイルには、セキュリティ対策が適用されません。そのため、WRITE_EXTERNAL_STORAGE パーミッションを持つアプリはすべて、このキャッシュのコンテンツにアクセスできます。

関連情報: キャッシュ ファイルを保存する

SharedPreferences をプライベート モードで使用する

getSharedPreferences() を使用してアプリの SharedPreferences オブジェクトの作成やアクセスを行う場合は、MODE_PRIVATE を使用します。これにより、共有環境設定ファイル内の情報にアクセスできるのが、デベロッパーのアプリだけになります。

アプリ間でデータを共有する場合は、SharedPreferences オブジェクトを使用しないでください。代わりに、アプリ間でデータをセキュアに共有するために必要な手順を行ってください。

関連情報: 共有環境設定を使用する

サービスと依存関係を最新の状態に保つ

ほとんどのアプリは、外部ライブラリとデバイス システム情報を使用して、専用のタスクを完了します。アプリの依存関係を最新の状態に保つことにより、このような通信ポイントをセキュアにすることができます。

Google Play 開発者サービスのセキュリティ プロバイダをチェックする

注: このセクションが当てはまるのは、Google Play 開発者サービスがインストールされているデバイスをターゲットとするアプリに限られます。

Google Play 開発者サービスを使用するアプリの場合、アプリがインストールされているデバイス上で Google Play 開発者サービスが更新されているか確認してください。このチェックは、UI スレッドとは非同期で実行する必要があります。デバイスが最新の状態でない場合、アプリは承認エラーをトリガーする必要があります。

アプリがインストールされているデバイス上で Google Play 開発者サービスが最新の状態かどうかを判断するには、SSL の脆弱性攻撃から保護するためにセキュリティ プロバイダをアップデートするで説明されている手順を行います。

関連情報:

アプリの依存関係をすべて更新する

アプリをデプロイする前に、ライブラリや SDK、その他すべての依存関係が最新の状態であるか確認してください。

  • Android SDK など、ファースト パーティ依存関係については、SDK Manager など、Android Studio 内にある更新ツールを使用します。
  • サードパーティ依存関係については、アプリが使用するライブラリのウェブサイトをチェックして、利用可能なアップデートやセキュリティ パッチをインストールします。

関連情報: ビルド依存関係の追加

詳細情報

アプリをセキュアにする方法については、以下の参考リンクをご覧ください。

参考情報

アプリをセキュアにする方法については、以下の参考リンクをご覧ください。

Codelab

ブログ