在内容解析中使用不受控制的数据¶
ID: java/android/unsafe-content-uri-resolution
Kind: path-problem
Security severity: 7.5
Severity: warning
Precision: high
Tags:
- security
- external/cwe/cwe-441
- external/cwe/cwe-610
Query suites:
- java-code-scanning.qls
- java-security-extended.qls
- java-security-and-quality.qls
当 Android 应用程序想要访问内容提供者中的数据时,它会使用 ContentResolver
对象。 ContentResolver
通过使用带有 content://
方案的 URI 与实现 ContentProvider
接口的类的实例进行通信。传递给 ContentResolver
的 URI 的 authority 部分(第一个路径段)决定了联系哪个内容提供者来执行操作。作用于文件的特定操作也支持 file://
方案,在这种情况下,会查询本地文件系统。如果外部组件(例如恶意或受损的应用程序)控制了 ContentResolver
操作的 URI,它可以欺骗易受攻击的应用程序访问其自己的私有文件或未导出的内容提供者。攻击应用程序可能能够通过强制将文件复制到公共目录(如外部存储)或通过使应用程序用意外数据覆盖文件来篡改内容来访问该文件。
建议¶
如果可能,避免使用外部提供的数据来确定要使用的 ContentResolver
的 URI。如果这不是一个选择,请验证传入的 URI 只能引用受信任的组件,例如内容提供者和/或应用程序的白名单,或者验证 URI 是否不引用私有目录,例如 /data/
。
示例¶
此示例展示了使用 ContentResolver
打开文件的三种方法。在第一种情况下,来自意图的外部提供的数据直接用于文件读取操作。这允许攻击者提供形式为 /data/data/(易受攻击的 应用程序包)/(私有 文件)
的 URI,以诱使应用程序读取并将其复制到外部存储。在第二种情况下,对外部提供的 URI 进行的检查不足,仍然存在利用空间。在第三种情况下,在使用 URI 之前对其进行正确验证,确保它不引用任何内部应用程序文件。
import android.content.ContentResolver;
import android.net.Uri;
public class Example extends Activity {
public void onCreate() {
// BAD: Externally-provided URI directly used in content resolution
{
ContentResolver contentResolver = getContentResolver();
Uri uri = (Uri) getIntent().getParcelableExtra("URI_EXTRA");
InputStream is = contentResolver.openInputStream(uri);
copyToExternalCache(is);
}
// BAD: input URI is not normalized, and check can be bypassed with ".." characters
{
ContentResolver contentResolver = getContentResolver();
Uri uri = (Uri) getIntent().getParcelableExtra("URI_EXTRA");
String path = uri.getPath();
if (path.startsWith("/data"))
throw new SecurityException();
InputStream is = contentResolver.openInputStream(uri);
copyToExternalCache(is);
}
// GOOD: URI is properly validated to block access to internal files
{
ContentResolver contentResolver = getContentResolver();
Uri uri = (Uri) getIntent().getParcelableExtra("URI_EXTRA");
String path = uri.getPath();
java.nio.file.Path normalized =
java.nio.file.FileSystems.getDefault().getPath(path).normalize();
if (normalized.startsWith("/data"))
throw new SecurityException();
InputStream is = contentResolver.openInputStream(uri);
copyToExternalCache(is);
}
}
private void copyToExternalCache(InputStream is) {
// Reads the contents of is and writes a file in the app's external
// cache directory, which can be read publicly by applications in the same device.
}
}