Post

App Links 及其安全性

在 Android 上 Google 提供了一套名为 App LinksDeep Links 的由 URI/URL 跳转到原生 Android 应用组件的协议及实现,使得用户从能够从原生应用(包括浏览器页面)直接通过链接跳转到指定的应用内容。本文详细介绍了它们如何运作,并剖析了其中的安全性。

App Links 是一种特殊类型的 Deep Links,可以看作是 Deep Links 的升级版本(Android 6.0 引入)。二者主要区别在于:当用户点击 App Links 时将直接跳转到应用内容,而 Deep Links 则会弹出应用选择对话框(如果用户设备上有多个应用可以处理相同的 intent)。举例来说:在电子邮件中点击银行发送的 HTTP 网址,如果是 Deep Link 系统可能会显示一个对话框,询问用户是使用浏览器还是银行自己的应用打开此链接,而 App Link 则直接跳转到应用。

此外二者的形式上也存在区别:App Links 和传统 HTTP URL 链接格式保持一致,并且一定关联于某一个 Web 站点 ,这意味着当用户没有安装实体应用时,App Links 就直接呈现为 Web 网站内容,而不会出现 404 页面或异常;Deep Links 则更加宽泛,它的 URI 在 http/https 协议之外还支持自定义 scheme ,意味着如果是自定义 schema,Deep Links 虽可在浏览器中使用,但却不指向 Web 网站内容,且当应用不存在时,跳转也将失效。

一言以概之,由于只支持 http/https 协议并关联到网站,App Links 可以简单理解为传统网站内容的应用版本。

在开发层面,创建一个完整的 App Links 包括:

  1. 遵循完整的 Deep Links 创建过程 :在 Android 应用清单中为 URI 创建对应的 intent 过滤器,即在目标 Activity 的 intent-filter 中,data 标签下配置一个或多个 URI(URI 支持细粒度路径,从而让不同 Activity 匹配不同 URI 路径),并且将应用设置为能够正确处理包含来自网站内容的 intent。创建 App Links 的要点仅仅是前述 URI 只能为 http/https 协议。

  2. 要求应用和网站之间建立认证关联,这是网站上发布 Digital Asset Links JSON 文件实现的,在安全性中将展开说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<activity
    android:name="com.example.android.GizmosActivity"
    android:label="@string/title_gizmos" >
    <intent-filter android:label="@string/filter_view_http_gizmos">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- Accepts URIs that begin with "http://www.example.com/gizmos” -->
        <data android:scheme="http"
              android:host="www.example.com"
              android:pathPrefix="/gizmos" />
        <!-- note that the leading "/" is required for pathPrefix-->
    </intent-filter>
    <intent-filter android:label="@string/filter_view_example_gizmos">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- Accepts URIs that begin with "example://gizmos” -->
        <data android:scheme="example"
              android:host="gizmos" />
    </intent-filter>
</activity>

引用官网的表格,完整展示二者的区别:

 Deep LinkApp Link
intent 网址协议httphttps 或自定义协议需要 httphttps
intent 操作任何操作需要 android.intent.action.VIEW
intent 类别任何类别需要 android.intent.category.BROWSABLEandroid.intent.category.DEFAULT
链接验证需要通过 HTTPS 协议在您的网站上发布 Digital Asset Links 文件
用户体验可能会显示一个消除歧义对话框,以供用户选择用于打开链接的应用无对话框;您的应用会打开以处理您的网站链接
兼容性所有 Android 版本Android 6.0 及更高版本

安全性

因为在逻辑上,App Links 是「网站内容的应用版本」或「网站内容的扩展部分」,并且 Google 的实现要做到无干预跳转,当用户从网站(App Links)跳转到应用时就必须避免用户跳转到仿冒的应用上。同样是上面银行邮件的例子,如果任何应用都能轻易地被作为该银行应用跳转,将造成严重安全问题。也就是说,App Links 最重要的安全特性是对应用进行身份认证。

Google 具体是这样实现网站对应用的认证的:

当应用关联的 Deep Link 想要升级为 App Link(即成为指定 URL 的默认处理程序)时,要在 AndroidManifest Activity 的 intent 过滤器中设置 android:autoVerify="true"

1
2
3
4
5
6
7
8
9
10
11
    <application>
      <activity android:name=”MainActivity”>
        <intent-filter android:autoVerify="true">
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="https" android:host="www.example.com" />
          <data android:scheme="https" android:host="mobile.example.com" />
        </intent-filter>
      </activity>
    </application>

随后 Android 系统(6.0 及以上版本,安装应用时)就会验证该 intent 过滤器中每一个域名所对应的服务器,即查询并校验相应服务器上位于 https://hostname/.well-known/assetlinks.json 的 Digital Asset Links 文件。

assetlinks.json 的内容

1
2
3
4
5
6
7
8
9
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example",
    "sha256_cert_fingerprints":
    ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
  }
}]

注:一个网站可与多个应用相关联,一个应用也可以关联到多个网站:如果关联多个子域名,则需要在每个子域名上放置一个有效的 assetlinks.json;如果采用通配符声明域名(如 *.example.com)则只需在根域名下放置 assetlinks.json 即可。

其中, package_name 为在应用的 build.gradle 文件中声明的应用 ID,sha256_cert_fingerprints 为应用签名证书的 SHA256 指纹。

这样,通过指定应用的证书指纹,网站就与经过 Android 系统校验的合法的应用关联在一起了,这种关联(认证)基于应用的数字签名,只要开发者签名私钥安全保管,同时Android系统取到正确的 assetlinks.json ,安全性就可以保证。

当然,为了保证系统能取到正确的 assetlinks.json ,Google 只允许 assetlinks.json 通过 https 访问,无论开发者设置的 App Link 协议是 http 还是 https

这样,经过严格的认证措施,任何未经验证的应用都无法通过仿冒合法应用的方式来被 App Link 跳转。

This post is licensed under CC BY 4.0 by the author.