简体   繁体   English

安全地限制对FileProvider的访问

[英]Securely restricting access to FileProvider

How can one create two Android apps where app "A" creates files and only app "B" can read the files? 一个人如何创建两个Android应用程序,其中应用程序“ A”创建文件,而只有应用程序“ B”可以读取文件? At first glance, Context.grantUriPermission seems like the trick because it allows restricting access to certain packages like so: 乍一看, 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);

(The above is from " How to use support FileProvider for sharing content to other apps? ") (以上内容来自“ 如何使用支持FileProvider来与其他应用共享内容? ”)

Unfortunately, anyone can create an app with a given package name. 不幸的是,任何人都可以使用给定的包名称来创建应用。 Therefore this method of restricting is not very secure. 因此,这种限制方法不是很安全。

Is there a more secure method for restricting file access? 有没有更安全的方法来限制文件访问? Perhaps by retrieving the public-key certificate of the calling application (app "B" in this case)? 也许通过检索调用应用程序(在这种情况下为应用程序“ B”)的公钥证书?

There isn't a truly reliable way of doing what you are looking for. 没有真正可靠的方式来完成您要寻找的东西。

  1. grantUriPermission seems to be a very simple way of doing so. grantUriPermission似乎是一种非常简单的方法。 It is a service the android system supports, so it's already there for you to use. 这是android系统支持的服务,因此已经可以使用。 As you stated, anyone can create an app with the required package name, though remember, they have to figure out what package name is required. 如您所述,任何人都可以使用所需的程序包名称创建应用程序,但是请记住,他们必须弄清楚所需的程序包名称。

  2. Another approach can be plain old encryption. 另一种方法可以是普通的旧加密。 This method is very similar to the above method where the encryption key is the package name. 此方法与上述方法非常相似,其中加密密钥是程序包名称。 It can end up being more secure because seeing what app can access the file might be easier that figuring out the encryption key. 最终可能会变得更加安全,因为查看哪些应用程序可以访问文件可能比找出加密密钥更容易。

  3. You can look into content providers . 您可以调查内容提供商 Basically, content providers are a method to share your database with other apps, and using code to control access to it. 基本上,内容提供者是一种与其他应用程序共享数据库并使用代码控制对数据库的访问的方法。 While we're talking about accessing a file, I assume whatever the file stores, a database can store as well, in a more neat and hidden way. 当我们谈论访问文件时,我假设无论文件存储了什么,数据库也可以以更简洁和隐藏的方式存储。

  4. The FileProvider has a feature of file request and approval. FileProvider具有文件请求和批准的功能。 The documentation is here . 文档在这里 This doesn't seem like the solution you a seeking because I'm not sure whether the stored file has any protections at all while stored (eg encryption), only access control. 这似乎不是您寻求的解决方案,因为我不确定存储的文件在存储时是否有任何保护(例如加密),只有访问控制。

Take note: it is surprisingly simple to decompile an app and access its code and resources, among them is the source code, encryption key, database login and more. 请注意:反编译应用程序并访问其代码和资源非常简单,其中包括源代码,加密密钥,数据库登录等。 Given the app is complex enough, it'll take a lot of time to reverse engineer what is going on in there but given enough time, and an attacker with enough patience, it can be done. 鉴于该应用程序足够复杂,需要花很多时间来对其中发生的事情进行逆向工程,但是如果有足够的时间,并且攻击者有足够的耐心,就可以完成。 Because of that, a 100% secure method with no vulnerabilities, simply does not exist. 因此,根本不存在没有漏洞的100%安全的方法。

Given enough time, any protection can be cracked. 如果有足够的时间,任何保护措施都可以被破解。 It is important to be able to identify the line between reasonable safeguards and paranoia. 能够确定合理的保障措施与偏执狂之间的界限非常重要。 To give my take on it, I'd have gone for a combination of grantUriPermission , due to the fact that upon uninstall the app's database is deleted, and encryption that is scattered throughout the application and oddly named classes to make reverse engineering more difficult (call me paranoid, I know). 为了说明这一点,我会结合使用grantUriPermission ,这是因为在卸载后,该应用程序的数据库将被删除,并且加密分散在整个应用程序和奇数命名的类中,从而使逆向工程更加困难(我知道偏执狂)

Good luck! 祝好运!

Perhaps by retrieving the public-key certificate of the calling application (app "B" in this case)? 也许通过检索调用应用程序(在这种情况下为应用程序“ B”)的公钥证书?

That is definitely possible: 绝对有可能:

  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);
  }

(from my SignatureUtils class ) (来自我的SignatureUtils

The result of getSignatureHash() is the same format that you get from the JDK's keytool , so you can generate the appropriate hash, save it somewhere (eg, string resource), and compare it to the one you determine on the fly. getSignatureHash()的结果与您从JDK的keytool获得的格式相同,因此您可以生成适当的哈希,将其保存在某个位置(例如,字符串资源),然后将其与动态确定的哈希进行比较。 Then, if the app claiming to be B does not have the proper hash, you do not send it a Uri to use. 然后,如果声称为B的应用程序没有正确的哈希,则不会向其发送要使用的Uri

(note that I need to update this code to deal with APKs with multiple signatures, which is a thing now) (请注意,我需要更新此代码以处理具有多个签名的APK,这已经成为现实)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM