Robert Sesek | 250f67c | 2019-06-13 00:21:12 | [diff] [blame] | 1 | # Android IPC Security Considerations |
| 2 | |
| 3 | Generally Chrome communicates between its processes using the |
| 4 | [Mojo](../../mojo/README.md) [inter-process communication (IPC) |
| 5 | mechanism](mojo.md). For most features, this is the preferred IPC mechanism to |
| 6 | use. However, as an Android application, there are certain interactions with |
| 7 | other applications and the Android OS that necessitate using different IPC |
| 8 | mechanisms to communicate. This document covers security concerns related to |
| 9 | those Android-specific IPC mechanisms. |
| 10 | |
| 11 | The Chrome browser process is typically the only process type that will interact |
| 12 | with these different IPC mechanisms. |
| 13 | |
| 14 | ## Intents |
| 15 | |
| 16 | [Intents](https://developer.android.com/guide/components/intents-filters) are |
| 17 | the most common type of inter-process communication mechanism on Android. They |
| 18 | are most commonly used to start Activities and they internally carry data |
| 19 | associated with that Activity (e.g. using the `ACTION_SEND` Intent to share a |
| 20 | piece of content and including either text or image data in the Intent body). |
| 21 | |
| 22 | ### Inbound Intents |
| 23 | |
| 24 | Because any application can dispatch Intents with Chrome as the receiver, when |
| 25 | receiving an inbound Intent, you should never fully trust the data contained |
| 26 | within. Data sent from other applications could be malicious or malformed, and |
| 27 | so you must validate or sanitze the data before passing it to other trusted |
| 28 | components of the browser process. Intents are handled in Java though, so |
| 29 | following the [Rule of 2](rule-of-2.md) is generally easy. (Though take note |
| 30 | that certain Android classes are just Java wrappers around native code, which |
| 31 | would not be considered safe by that rule.) |
| 32 | |
Robert Sesek | 5325333 | 2022-10-28 20:06:16 | [diff] [blame] | 33 | Inbound Intents may also pose deserialization issues via the data stored in an |
| 34 | Intent's extras. These issues may result in non-exploitable crashes (e.g. |
| 35 | https://crbug.com/1232099), but it is also possible to have deserialization |
| 36 | vulnerabilities with security implications. Always use the |
| 37 | [`IntentUtils.safe*Extra()`](https://source.chromium.org/chromium/chromium/src/+/main:base/android/java/src/org/chromium/base/IntentUtils.java;l=58;drc=7f1297bacd32fe668d4c99cb8963b56aed363acc) |
| 38 | family of methods to access Intent extra fields from inbound Intents. |
| 39 | |
Robert Sesek | 277dd72 | 2021-05-24 21:24:07 | [diff] [blame] | 40 | It is **fundamentally impossible** to determine the sender of an Intent, unless |
| 41 | the Activity was started with |
| 42 | [`startActivityForResult`](https://developer.android.com/reference/android/app/Activity#startActivityForResult(android.content.Intent,%20int)). |
| 43 | For Intents that are started via `startActivityForResult`, you can use |
| 44 | [`getCallingActivity`](https://developer.android.com/reference/android/app/Activity#getCallingActivity()) |
| 45 | or |
| 46 | [`getCallingPackage`](https://developer.android.com/reference/android/app/Activity#getCallingPackage()) |
| 47 | to retrieve the identity of the component that called |
| 48 | [`setResult`](https://developer.android.com/reference/android/app/Activity#setResult(int)) |
| 49 | on the started Activity. For all other cases, the security model of your feature |
| 50 | cannot depend on authenticating the sender of an Intent. Do not trust |
| 51 | `Intent.EXTRA_REFERRER`. See also the discussion below about [capability |
| 52 | tokens](#capability-tokens). |
Robert Sesek | 250f67c | 2019-06-13 00:21:12 | [diff] [blame] | 53 | |
| 54 | One way to authorize Intents is to use the system's |
| 55 | [`android:permission`](https://developer.android.com/guide/topics/permissions/overview#permission_enforcement) |
| 56 | attribute on a component's (e.g. Activity, Service, etc.) manifest declaration. |
| 57 | You can [define a custom permission](https://developer.android.com/guide/topics/permissions/defining) and |
| 58 | set the `android:protectionLevel` of the permission to `"signature"` or |
| 59 | `"signatureOrSystem"` to restrict access to just components signed by the same |
| 60 | certificate (or trusted system components). |
| 61 | |
| 62 | ## Outbound Intents {#outbound-intents} |
| 63 | |
| 64 | There are [two types of Intents](https://developer.android.com/guide/components/intents-filters?hl=en#Types): |
| 65 | implicit and explicit. With implicit Intents, the receiving application is not |
| 66 | specified by the sender and the system uses a resolution process to find the |
| 67 | most suitable component to handle it. An implicit Intent can sometimes result in |
| 68 | a chooser being shown to the user when multiple applications could handle it. |
| 69 | Explicit Intents specify either the package name or a fully qualified |
| 70 | `ComponentName`, so the recipient is known at the time it is dispatched. |
| 71 | Implicit Intents can result in an unexpected (and maybe malicious) application |
| 72 | receiving user data. If it is possible to know the target application when |
| 73 | sending an Intent, always prefer using an explicit Intent. |
| 74 | |
| 75 | ## PendingIntents |
| 76 | |
| 77 | A [PendingIntent](https://developer.android.com/reference/android/app/PendingIntent) |
| 78 | is created by one application and vended to another. The object allows the |
| 79 | receiving application to start the component (i.e. Activity, Service, Broadcast) |
| 80 | _as if the creating application started it_. Similar to a [setuid binary](https://en.wikipedia.org/wiki/Setuid), |
| 81 | you must use this with care, as it can even be used to start non-exported |
| 82 | components of the creating application. |
| 83 | |
| 84 | It is possible to retrieve information about the creator package of the |
| 85 | PendingIntent using the [`getCreatorPackage()`](https://developer.android.com/reference/android/app/PendingIntent.html#getCreatorPackage()) |
| 86 | method. This is the identity under which the Intent, which the PendingIntent |
| 87 | represents, will be started. Note that you cannot retrieve specific information |
| 88 | about the Intent (e.g. its target and extras). And as discussed above with |
| 89 | Intents, it is not possible to determine the application that called |
| 90 | `PendingIntent.send()`. |
| 91 | |
| 92 | ## Binder |
| 93 | |
| 94 | [Binder](https://developer.android.com/reference/android/os/Binder) is the low |
| 95 | level IPC mechanism on Android, and it is what Intents and other Framework-level |
| 96 | primitives are built upon. |
| 97 | |
| 98 | ### Bound Services |
| 99 | |
| 100 | To communicate between components using Binder, you declare a `<service>` in |
| 101 | your manifest and connect to it using [`Context.bindService()`](https://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent,%2520android.content.ServiceConnection,%2520int)). |
| 102 | This is referred to a as a [bound service](https://developer.android.com/guide/components/bound-services). |
| 103 | |
| 104 | One of the powerful properties of a bound service is that you can determine the |
| 105 | identity of your communicating peer. This can only be done during a Binder |
| 106 | transaction (e.g. in an [AIDL](https://developer.android.com/guide/components/aidl) |
| 107 | method implementation or a [`Handler.Callback`](https://developer.android.com/reference/android/os/Handler.Callback.html)) |
| 108 | that is **not** marked [`FLAG_ONEWAY`](https://developer.android.com/reference/android/os/IBinder). |
| 109 | During the transaction use [`Binder.getCallingUid()`](https://developer.android.com/reference/android/os/Binder.html#getCallingUid()) |
| 110 | to retrieve the package's UID. |
| 111 | |
| 112 | In Android, every installed application is given a unique user ID (UID). This |
| 113 | can be used as a key to query the [PackageManager](https://developer.android.com/reference/android/content/pm/PackageManager), |
| 114 | to retrieve the [PackageInfo](https://developer.android.com/reference/android/content/pm/PackageInfo) |
| 115 | for the application. With the PackageInfo, information about the applications |
| 116 | code signing certificates can be retrieved and cryptographically authenticated. |
| 117 | This is a strong authentication check and it is the **only** reliable mechanism |
| 118 | by which you can authenticate your peer. |
| 119 | |
| 120 | In Chrome, the helper functions |
| 121 | [`ExternalAuthUtils.isCallerValid()`](https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java?l=157&rcl=fa790f69ce80bf2e192d710ea08b8343cad93fbb) |
| 122 | and `isCallerValidForPackage()` can perform these checks for you. |
| 123 | |
| 124 | ## Capability Tokens {#capability-tokens} |
| 125 | |
| 126 | We define a **capability token** to be an unforgeable object that the holder may |
| 127 | present to another application as authentication to access a specific |
| 128 | capability. Binder objects are backed by the kernel (i.e. are unforgeable), are |
| 129 | transferable, and are comparable using `isEqual()`, so Binders can be used as |
| 130 | capability tokens. |
| 131 | |
| 132 | One security factor to bear in mind is that because capability tokens are |
| 133 | transferable, they do not strongly authenticate a caller's identity. One |
| 134 | application may deliberately or accidentally transfer a capability token to |
| 135 | another application, or a token could be exfiltrated via an application logic |
| 136 | vulnerability. Therefore, only use capability tokens for access control, not |
| 137 | identity authentication. |
| 138 | |
| 139 | While noting the above factor, capability tokens can be useful for |
| 140 | authenticating Intents. If two applications have established a Binder |
| 141 | connection, they can use the channel to exchange a capability token. One |
| 142 | application constructs a generic Binder (using the |
| 143 | [`Binder(String)`](https://developer.android.com/reference/android/os/Binder.html#Binder(java.lang.String)) |
| 144 | constructor) and sends the object over that `ServiceConnection` to the other |
| 145 | application, while retaining a reference to it. |
| 146 | |
| 147 | The generic Binder object can then be transmitted as an Intent extra when |
| 148 | sending Intents between the two applications. By comparing the object with |
| 149 | `Binder.isEqual()`, you can validate the capability token. Be sure to use an |
| 150 | [explicit Intent](#outbound-intents) when sending such an Intent. |
| 151 | |
| 152 | This same approach can also be done with using a PendingIntent to a non-exported |
| 153 | component as a capability token. Internally PendingIntents use a Binder token |
| 154 | approach, so the only significant difference is the additional capability |
| 155 | conferred by the PendingIntent to start a component. |