Deep Link与URL Scheme安全:自定义Scheme的劫持风险、验证Deep Link的Host与Path、防止恶意应用注册相同Scheme
大家好,我是你们的老朋友。今天我们来聊聊Android组件安全里一个特别容易踩坑的话题——Deep Link和URL Scheme的安全问题。说实话,我在做安全审计的时候,十有八九都能在App里找到Scheme相关的漏洞。这玩意儿看着简单,但坑是真的多。
一、自定义Scheme的劫持风险
先说说什么是URL Scheme。说白了,它就是App的“身份证号”。比如你打开淘宝,用的就是taobao://这个Scheme。但问题来了——这个“身份证号”是随便写的,谁都可以注册。
我在项目中遇到过这样一个案例:某支付App使用了mypay://作为Scheme。结果有个恶意应用也注册了mypay://,而且它的Activity优先级更高。用户点击支付链接时,直接跳到了钓鱼页面。嗯,这就是典型的Scheme劫持。
为什么会这样?因为Android系统在处理Scheme时,遵循的是“先到先得”原则。如果多个App声明了同一个Scheme,系统会弹窗让用户选择。但用户哪分得清哪个是真的?
二、验证Deep Link的Host与Path
那怎么防?我的建议是:永远不要只依赖Scheme做路由。一定要加上Host和Path的验证。
你想想看,如果只是myapp://,那谁都能劫持。但如果是myapp://secure/login,你就能在代码里校验Host是不是你自己的域名,Path是不是合法的路径。
代码示例:
<activity android:name=".DeepLinkActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="myapp"
android:host="www.myapp.com"
android:pathPrefix="/secure" />
</intent-filter>
</activity>
注意这里的android:host和android:pathPrefix。我习惯把Host设成自己的官网域名,这样恶意应用就算注册了同样的Scheme,也没法伪造你的域名。
但光在Manifest里声明还不够。你还要在代码里做二次校验:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Uri data = intent.getData();
if (data != null) {
String host = data.getHost();
String path = data.getPath();
// 二次校验Host和Path
if (!"www.myapp.com".equals(host)) {
// 非法来源,直接关闭
finish();
return;
}
if (!path.startsWith("/secure")) {
// 路径不合法,拒绝处理
finish();
return;
}
// 安全通过,处理业务逻辑
handleDeepLink(data);
}
}
myapp://evil直接调起,传了个恶意参数进去。从那以后,我所有Deep Link的入口都加了双重校验。
三、防止恶意应用注册相同Scheme
这个问题其实挺棘手的。因为Android系统层面并没有提供“独占Scheme”的机制。你没法阻止别人注册跟你一样的Scheme。
那怎么办?我的思路是:让恶意应用注册了也没用。
具体做法有几种:
- 使用App Links(Android 6.0+):通过Digital Asset Links验证,只有你的App能处理特定域名下的链接。系统不会弹窗让用户选择。
- 增加签名校验:在Deep Link处理逻辑中,检查调用方的包名和签名。如果不是你自己的App,直接拒绝。
- 使用加密参数:在Deep Link的URL里带上加密的token,服务端验证token合法性。恶意应用伪造的链接没有合法token。
我个人最推荐的是App Links。它通过HTTPS验证你的域名所有权,系统会信任你的App。恶意应用就算注册了同样的Scheme,系统也不会把它列为候选。
配置App Links需要两步:
- 在服务器上放置
.well-known/assetlinks.json文件 - 在Manifest中声明
android:autoVerify="true"
<activity android:name=".DeepLinkActivity">
<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.myapp.com"
android:pathPrefix="/deep" />
</intent-filter>
</activity>
四、知识体系结构图
下面这张图总结了Deep Link安全的核心逻辑,我画了很久才理清楚这些关系:
五、避坑指南
最后,分享几个我踩过的坑:
- 不要用纯数字Scheme:比如
123://,有些系统版本会把数字Scheme当成电话号码处理,导致链接无法正常跳转。 - 注意大小写:Scheme是大小写敏感的。
MyApp://和myapp://是两个不同的Scheme。我建议统一用小写。 - 别把敏感数据放URL里:Deep Link的URL可能会被系统日志记录,也可能被其他App截获。token、密码这类东西千万别放进去。
好了,关于Deep Link和URL Scheme的安全,今天就聊这么多。记住一句话:Scheme是门,Host是锁,Path是钥匙。门谁都能装,但锁和钥匙得你自己配好。