FileProvider 共享目录配置不当风险解析与安全建议

55 阅读5分钟

一、业务场景

FileProvider 是 ContentProvider 的一个特殊子类,通过生成 content:// URI(而非 file:/// URI)来安全地实现应用间的文件共享。

典型配置如下:

<application ...>

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.example.demo.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">

    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths" />
</provider>

res/xml/provider_paths.xml 文件如下:

解释如下:
  • files-path → 映射到 Context.getFilesDir()

    即路径:/data/user/<user_id>/<package>/files/

  • path=“share” → 允许共享 files/share/ 子目录 path=“share” → 允许共享 files/share/ 子目录

  • name=“shared_files” → 仅为规则名称,不影响访问路径或权限

当应用通过以下代码获取 URI:

Uri uri = FileProvider.getUriForFile(
    this,
    "com.example.demo.fileprovider",
    file
);

最终可共享的实际目录为: /data/user/<user_id>/<包名>/files/share/

二、配置不当及风险分析

  • 标签配置过宽风险

provider_paths.xml 中除了 files-path 之外,还有其他标签可定义根目录,例如 root-path、external-path 等,全部标签定义与风险如下表所示。

paths 标签对应 Context / API(系统实际调用)实际共享路径示例(user 0)风险等级与原因说明
root-path无,直接指向系统根目录 '/'/极高:理论上可映射任意路径,配置不当 = 全盘裸奔
files-pathContext.getFilesDir()/data/user/0/[package]/files较低:应用私有目录,通常可控,官方推荐为共享根目录
cache-pathContext.getCacheDir()/data/user/0/[package]/cache较低:私有缓存目录,但文件不长期存储,泄露影响较小
external-pathEnvironment.getExternalStorageDirectory()/sdcard/极高:暴露整个外置存储,攻击者可越权读取大量无关文件
external-files-pathContext.getExternalFilesDirs(null)/sdcard/Android/data/[package]/files中等:虽为 App 专属目录,但可能包含大量业务数据,泄露面较大
external-cache-pathContext.getExternalCacheDirs()/sdcard/Android/data/[package]/cache中等:缓存内容不可控,易形成侧信道或残留敏感数据泄露
external-media-pathContext.getExternalMediaDirs()/sdcard/Android/media/[package]/中~偏高:多存放媒体文件,若配置过宽可能暴露用户照片/视频等隐私资源
  • path路径配置不当风险

上述是标签的定义,但实际上,要分享的目录还进一步去取决于path的定义,path 的设置直接决定了共享权限的边界。

FileProvider 最终生效的共享目录 = 标签对应的基础路径 + path 属性指定的子路径。

例如: <files-path name="shared_files" path="share" />实际共享目录为:/data/user/0/[package]/files/share/


如果 path 设置为 空字符串 ("")点 (.)斜杠 (/) ,则 FileProvider 不会追加任何子路径,导致:

-》最终共享目录 = 标签对应的整个基础目录

-》共享范围过大

-》同目录下的所有文件均可能被其他应用访问

-》直接造成数据泄露风险

以files-path为例,其基础路径为:/data/user/0/<package>/files,path设置为空字符串 ("")点 (.)斜杠 (/),其最终的路径都是/data/user/0/<package>/files

三、攻击场景

标签与path路径结合定义出来的目录范围过宽,并不会立刻产生漏洞,要真正形成漏洞,需要以下 两个条件同时成立

  1. 可被外部攻击者控制的文件名 / 文件路径输入
  2. 应用存在实际的共享行为(如分享 Intent 或导出 URI),授权给外部恶意应用 如果当前业务没有第 2 点,即便路径配置过宽,也不会直接导致越权。但不符合最小权限原则,属于必须整改的高危配置。

满足以上两个条件,可能会被攻击者利用,如下三个场景:

场景 1:恶意 App 劫持分享 Intent(Activity Hijacking)

攻击链:

  1. 受害 App 构造分享文件的 Intent

  2. 系统匹配可接收的 Activity

  3. 恶意 App 注册高优先级接收器

  4. 恶意 App 抢占 Intent → 自动获得 READ 临时权限

影响:可直接读取 FileProvider 暴露的全部目录内容


场景 2:恶意 App 强制触发分享逻辑

攻击方式:

  1. 构造特殊 deep link

  2. 引导受害 App 打开页面

  3. 自动触发分享逻辑

  4. 系统为恶意 App 授予读取权限

风险:宽松路径可能使整个目录被读取


场景 3:宽松路径放大业务未来迭代的风险

即便当前没有文件名可控输入没有对外分享行为,宽松配置依旧是潜在隐患:

只要未来版本中添加了“对外共享文件”的功能,或用户可控文件名,漏洞就会立刻被触发。

这也是安全审计中最常见的风险放大点。


四、安全建议(强制)

1. 禁用高风险标签

以下标签应避免使用:

  • root-path

  • external-path

原因:这两个目录范围过大,极易造成无意中共享整个文件系统区域。


2. 严格限制 path 值

禁止:

  • path=""

  • path="."

  • path="/"

推荐:

  • 明确限定子目录,例如:
<files-path name="secure_images" path="images_secure/" />
<cache-path name="tmp_export" path="export_tmp/" /> 

3. 最小化授权(强制)

  • android:exported="false"
  • 必须使用 grantUriPermissions="true" 才能对指定接收者动态授权
  • 不允许全局向任意 App 暴露访问权限

五、风险排查方法

排查步骤:

  1. 检查 AndroidManifest 中的 FileProvider 定义

  2. 定位其对应的 provider_paths.xml

  3. 查找是否存在以下危险配置:

    • external-path

    • root-path

    • path=""、 "."、"/"

若发现宽松配置,进一步核实:

  • 是否存在用户可控 filename
  • 是否存在分享行为(例如 ACTION_SEND)
  • 是否存在导出 URI 给三方的流程
  • 是否存在可被外部 App 触发的自动分享机制

即使当前不存在漏洞,也需按安全要求立即整改。