Secure data transfer between two applications

    Hello everyone, today I would like to tell you about some options for transferring data between two android applications and consider them from a security point of view. I decided to write this article for two reasons. First, I often began to encounter a lack of understanding of the developers of the mechanisms for working with the components of an android application. The second - I stopped understanding what the choice of this or that mechanism is based on when implementing features and wanted to convey how it should look at the minimum.

    Task

    We have 2 applications that access the same API. Clients can access the API by access token (sessionId). You must implement a seamless transition from one application to another. To do this, you need to fumble between them, for example, let it be sessionId.

    Option # 1: QUERY DEEPLINK

    The most obvious option is to transfer the token to Query DeepLink. It will look something like this:
    slave://main?sessionId=686A885A4FB644053C584B9BE2A70C7D
    In this case, the recipient will be able to extract sessionId and use it without requesting authorization from the user. From the developer's side, it looks like the task has been completed, but let's dig a little deeper.

    Deeplink hijacking


    Since any application can register the tinkoff: // scheme, the OS can open the wrong application. This is possible due to the fact that there is no registration and restrictions on the use of schemes. A malicious application may register the tinkoff: // scheme and intercept the request to the Tinkoff application and start itself. In this case, sessionId will fall into the wrong hands and your account will be compromised. In addition, DeepLink Hijacking allows you to conduct phishing, for example, displaying fields for entering a username and password.

    Conceptually, the process looks like this:

    image

    There are 2 solutions to this problem. First, AppLinks technology no longer allows developers to customize the scheme; instead, http / https is used. In this case, the OS takes the link slave.com/profileand contacts the host slave.com for verification. The second - Intent URL - instead of calling slave: //, intent: // is called, where the unique identifier of the application to be launched is passed. It looks like this:

    intent://main/#Intent;scheme=slave;package=com.example.slave.client.android;end"

    In this case, it will not be possible to intercept the launch of the application, since a specific package is specified. Still, the problem remains that the user can install the application from a third-party source with the same packageId as your slave. In this case, if you did not have a legitimate slave application, the malicious application will install and receive your token.

    Session fixation


    This is an attack in which an attacker forces the client to establish a session with the target software using the sessionId provided by the attacker. As soon as the user authenticates, the attacker will be able to use this already privileged identifier for his own purposes. The attack exploits the fact that the target software uses the same sessionId after privilege escalation.


    How it looks in our case:

    1. attacker gets anonymous session from application
    2. throws beautifully a letter to the victim on behalf of the bank, in which he is invited to go to his personal account
    3. when clicking on the link, we get to DeepLink with an attacker session slave: // main? sessionId = 686A885A4FB644053C584B9BE2A70C7D
    4. the mobile application takes a session, understands that it does not have enough rights and asks the user to authenticate
    5. the user passes it, the session has increased rights
    6. user in the application, an attacker with a privileged session, profit

    It would be correct to fix this on the API, issuing another sessionId after privilege escalation, but we are writing a mobile application. And our way is to refuse to transfer the token from master to slave. Plus, this will give us in-depth protection and if something breaks on the API and the tokens will not change when privileges are increased, then an attack will still be impossible.

    3rd party leakage


    Another minus of this option. Many people use third-party services for DeepLink because of the convenience of generating links, analytics and other cool stuff. In this case, you simply give your token to a third-party company.

    Option # 2: CONTENT PROVIDER

    How will we do it? We define the master Content-Provider and make slave go to this Content-Provider for the token.



    Thus, we get rid of the risk of transferring the token to the wrong application in the case of DeepLink Hijacking and make the Session Fixation attack impossible. But we have other problems - in the current version, in general, any application can request a token at any time, even if we did not initiate its launch.

    Protection level


    In most cases, you need to verify that the slave is signed with the same key as master, that is, they belong to the same author. For this case, the package manager has a checkSignatures method that checks application signatures. To use this function, you need to add permission with protectionLevel = "signature" in the Content-Provider in the application manifest:


    The scheme will hardly change from the previous figure, only a guarantee will appear that only applications with a signature from the same author will get access to the token.

    Permission Race Condition


    There is one very unpleasant feature that permission names are not unique, which can be used by a malicious application and register permission with our name and protectionLevel = "normal" before us. In this case, when installing our application, permission will already exist on the OS and it will not be overwritten. Consequently, our content-provider will remain unprotected and with authorized access from any application.

    Different signatures


    Unfortunately, far from always applications are signed with one key, for example, some of the applications are purchased, or “so historically”, but a seamless transition is still needed. In this case, we take the signature verification on ourselves.
    How this can be implemented:
    Content-Provider has a getCallingPackage () method, by which we can get the packageId of the application that has applied for data, and by packageId we can get a list of signatures and check them with the built-in ones.

    String pkg = this.getCallingPackage();
    PackageInfo pkgInfo = pkgmgr.getPackageInfo(pkg, GET_SIGNATURES);
    Signatures[] signatures = pkgInfo.signatures;
    for (Signature sig: signatures) {
      if (sig.equals(TRUSTED_SIGNATURE)) {
        // trusted signature found, trust the application
      }
    }



    It seems that we did everything perfectly, but no.

    Fake id vulnerability


    The problem is that when Android creates a chain-of-trust, the verification process only compares the subject, and does not verify the signature in the certificate's signer field. As a result, an attacker can build chain-of-trust without an actual signature.

    Because of this error, an incorrect certificate chain is generated, which may include legitimate certificates embedded in the APK, but not actually used to sign the application. In the end, I’ll leave a link to the commit that fixes this vulnerability. The problem is fixed in android 4.4, so we can only raise the API Level to 19.

    conclusions

    Today we examined how features should be analyzed during development.
    We also examined the options for transferring the secret between two applications, during which we analyzed the problems of each option and came up with ways to avoid them.

    All secure applications!

    References


    Also popular now: