【问题标题】:Securely restricting access to FileProvider安全地限制对 FileProvider 的访问
【发布时间】:2017-02-22 20:38:46
【问题描述】:

如何创建两个应用程序“A”创建文件而只有应用程序“B”可以读取文件的 Android 应用程序?乍一看,Context.grantUriPermission 似乎是个窍门,因为它允许像这样限制对某些包的访问:

//grant permision for app with package "packageName", eg. before starting other app via intent
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

(以上来自“How to use support FileProvider for sharing content to other apps?”)

不幸的是,任何人都可以使用给定的包名称创建应用。所以这种限制方式不是很安全。

是否有更安全的方法来限制文件访问?也许通过检索调用应用程序(在本例中为应用程序“B”)的公钥证书?

【问题讨论】:

    标签: android android-contentprovider android-fileprovider


    【解决方案1】:

    没有一种真正可靠的方法可以满足您的需求。

    1. grantUriPermission 似乎是一种非常简单的方法。这是android系统支持的服务,所以已经可以使用了。正如您所说,任何人都可以使用所需的包名称创建应用程序,但请记住,他们必须弄清楚所需的包名称。

    2. 另一种方法是普通的旧加密。此方法与上述加密密钥为包名的方法非常相似。它最终会更加安全,因为查看哪些应用可以访问文件可能比找出加密密钥更容易。

    3. 您可以查看content providers。基本上,内容提供者是一种与其他应用程序共享数据库并使用代码控制对其访问的方法。当我们谈论访问文件时,我假设无论文件存储什么,数据库也可以存储,以更简洁和隐藏的方式。

    4. FileProvider 具有文件请求和批准的功能。文档是here。这似乎不是您寻求的解决方案,因为我不确定存储的文件在存储时是否有任何保护(例如加密),只有访问控制。

    请注意:反编译应用程序并访问其代码和资源非常简单,其中包括源代码、加密密钥、数据库登录等等。鉴于该应用程序足够复杂,对其中发生的事情进行逆向工程需要大量时间,但只要有足够的时间,并且攻击者有足够的耐心,就可以完成。正因为如此,没有漏洞的 100% 安全方法根本不存在。

    只要有足够的时间,任何保护都可以被破解。重要的是能够确定合理的保障措施和偏执狂之间的界限。为了给出我的看法,我会选择 grantUriPermission 的组合,因为卸载后应用程序的数据库被删除,加密分散在整个应用程序和奇怪的命名类中,以使逆向工程更多困难(叫我偏执狂,我知道)。

    祝你好运!

    【讨论】:

      【解决方案2】:

      也许通过检索调用应用程序(在本例中为应用程序“B”)的公钥证书?

      这绝对是可能的:

        public static String getSignatureHash(Context ctxt, String packageName)                                                                     
          throws NameNotFoundException, NoSuchAlgorithmException {
          MessageDigest md=MessageDigest.getInstance("SHA-256");
          Signature sig=
              ctxt.getPackageManager()
                  .getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures[0];
      
          return(toHexStringWithColons(md.digest(sig.toByteArray())));
        }
      
        // based on https://stackoverflow.com/a/2197650/115145
      
        public static String toHexStringWithColons(byte[] bytes) {
          char[] hexArray=
              { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
                  'C', 'D', 'E', 'F' };
          char[] hexChars=new char[(bytes.length * 3) - 1];
          int v;
      
          for (int j=0; j < bytes.length; j++) {
            v=bytes[j] & 0xFF;
            hexChars[j * 3]=hexArray[v / 16];
            hexChars[j * 3 + 1]=hexArray[v % 16];
      
            if (j < bytes.length - 1) {
              hexChars[j * 3 + 2]=':';
            }
          }
      
          return new String(hexChars);
        }
      

      (来自my SignatureUtils class

      getSignatureHash() 的结果与您从 JDK 的keytool 获得的格式相同,因此您可以生成适当的散列,将其保存在某处(例如,字符串资源),并将其与您确定的那个进行比较苍蝇。然后,如果自称 B 的应用没有正确的哈希值,则不要向其发送 Uri 以供使用。

      (请注意,我需要更新此代码以处理具有多个签名的 APK,这是现在的事情)

      【讨论】:

      • 谢谢!我没有立即看到 FileProvider 将在哪里接收包名称。你愿意详细说明一下吗?
      • @BrianRisk:我不明白你的问题。您知道自己的应用程序 ID (BuildConfig.APPLICATION_ID)。为此,您还需要知道其他应用程序的应用程序 ID。您如何处理(例如,将价值融入您的应用)取决于您。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-05
      • 2014-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多