CodeQL 文档

Android APK 安装

ID: java/android/arbitrary-apk-installation
Kind: path-problem
Security severity: 9.3
Severity: error
Precision: medium
Tags:
   - security
   - external/cwe/cwe-094
Query suites:
   - java-security-extended.qls
   - java-security-and-quality.qls

点击查看 CodeQL 存储库中的查询

Android 允许应用程序使用 MIME 类型为 "application/vnd.android.package-archive"Intent 来安装 Android 软件包套件 (APK)。如果 Intent 中使用的文件来自应用程序无法控制的位置(例如,可全局写入的 SD 卡),则可能导致意外安装不受信任的应用程序。

建议

您应该使用 PackageInstaller 类安装软件包。

如果需要从文件安装,则应使用 FileProvider。内容提供程序可以提供比文件系统权限更具体的权限。

当您的应用程序不需要安装软件包时,请勿在清单文件中添加 REQUEST_INSTALL_PACKAGES 权限。

示例

在以下(错误)示例中,软件包是从可能被其他应用程序更改的文件安装的

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;

import java.io.File;

/* Get a file from external storage */
File file = new File(Environment.getExternalStorageDirectory(), "myapp.apk");
Intent intent = new Intent(Intent.ACTION_VIEW);
/* Set the mimetype to APK */
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");

startActivity(intent);

在以下(正确)示例中,软件包是使用 FileProvider 安装的

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.core.content.FileProvider;

import java.io.File;
import java.io.FileOutputStream;

String tempFilename = "temporary.apk";
byte[] buffer = new byte[16384];

/* Copy application asset into temporary file */
try (InputStream is = getAssets().open(assetName);
     FileOutputStream fout = openFileOutput(tempFilename, Context.MODE_PRIVATE)) {
    int n;
    while ((n=is.read(buffer)) >= 0) {
        fout.write(buffer, 0, n);
    }
}

/* Expose temporary file with FileProvider */
File toInstall = new File(this.getFilesDir(), tempFilename);
Uri applicationUri = FileProvider.getUriForFile(this, "com.example.apkprovider", toInstall);

/* Create Intent and set data to APK file. */
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(applicationUri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

startActivity(intent);

在以下(正确)示例中,软件包是使用 android.content.pm.PackageInstaller 类的实例安装的

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller;

private static final String PACKAGE_INSTALLED_ACTION =
    "com.example.SESSION_API_PACKAGE_INSTALLED";

/* Create the package installer and session */
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params =
    new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
int sessionId = packageInstaller.createSession(params);
session = packageInstaller.openSession(sessionId);

/* Load asset into session */
try (OutputStream packageInSession = session.openWrite("package", 0, -1);
     InputStream is = getAssets().open(assetName)) {
    byte[] buffer = new byte[16384];
    int n;
    while ((n = is.read(buffer)) >= 0) {
        packageInSession.write(buffer, 0, n);
    }
}

/* Create status receiver */
Intent intent = new Intent(this, InstallApkSessionApi.class);
intent.setAction(PACKAGE_INSTALLED_ACTION);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.getIntentSender();

/* Commit the session */
session.commit(statusReceiver);

参考

  • ©GitHub 公司
  • 条款
  • 隐私