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
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);
参考¶
Android 开发者:Intent.ACTION_INSTALL_PACKAGE。
Android 开发者:Manifest.permission.REQUEST_INSTALL_PACKAGES。
Android 开发者:PackageInstaller。
Android 开发者:FileProvider。
常见缺陷列表:CWE-94。