CodeQL 文档

PreferenceActivity 中的 Android Fragment 注入

ID: java/android/fragment-injection-preference-activity
Kind: problem
Security severity: 9.8
Severity: error
Precision: high
Tags:
   - security
   - external/cwe/cwe-470
Query suites:
   - java-code-scanning.qls
   - java-security-extended.qls
   - java-security-and-quality.qls

点击查看 CodeQL 代码库中的查询

当使用外部提供的名称实例化 Fragment 时,这会将任何动态创建和托管 Fragment 的已导出 Activity 暴露给 Fragment 注入。恶意应用程序可以提供任意 Fragment 的名称,即使是未设计为可外部访问的 Fragment,并将其注入 Activity。这可以绕过访问控制并使应用程序面临意外影响。

Fragment 是 Android 应用程序用户界面的可重用部分。即使 Fragment 控制着自己的生命周期和布局,并处理其输入事件,它也不能独立存在:它必须由 Activity 或其他 Fragment 托管。这意味着,通常情况下,只有当托管 Fragment 的 Activity 本身是已导出时,第三方应用程序才能访问(即导出)Fragment。

建议

一般来说,除非已对名称进行正确验证,否则不要使用用户提供的名称实例化类(包括 Fragment)。此外,如果已导出的 Activity 正在扩展 PreferenceActivity 类,请确保已覆盖 isValidFragment 方法,并且仅当提供的 fragmentName 指向预期 Fragment 时才返回 true

示例

以下示例显示了两种情况:在第一种情况下,使用不受信任的数据实例化片段并将其添加到活动中,而在第二种情况下,使用静态名称安全地添加片段。

public class MyActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstance) {
        try {
            super.onCreate(savedInstance);
            // BAD: Fragment instantiated from user input without validation
            {
                String fName = getIntent().getStringExtra("fragmentName");
                getFragmentManager().beginTransaction().replace(com.android.internal.R.id.prefs,
                        Fragment.instantiate(this, fName, null)).commit();
            }
            // GOOD: Fragment instantiated statically
            {
                getFragmentManager().beginTransaction()
                        .replace(com.android.internal.R.id.prefs, new MyFragment()).commit();
            }
        } catch (Exception e) {
        }
    }

}

下一个示例显示了两个扩展了 PreferenceActivity 的活动。第一个活动重写了 isValidFragment,但它错误地无条件返回了 true。第二个活动正确地重写了 isValidFragment,因此它仅在 fragmentName 是受信任的片段名称时才返回 true

class UnsafeActivity extends PreferenceActivity {

    @Override
    protected boolean isValidFragment(String fragmentName) {
        // BAD: any Fragment name can be provided.
        return true;
    }
}


class SafeActivity extends PreferenceActivity {
    @Override
    protected boolean isValidFragment(String fragmentName) {
        // Good: only trusted Fragment names are allowed.
        return SafeFragment1.class.getName().equals(fragmentName)
                || SafeFragment2.class.getName().equals(fragmentName)
                || SafeFragment3.class.getName().equals(fragmentName);
    }

}

参考文献

  • ©GitHub 公司
  • 条款
  • 隐私