3步搞定ARouter路由安全:从URL Scheme漏洞到攻防实战
你是否遇到过这样的情况:用户点击一条看似正常的链接,却意外打开了App内部的敏感页面?或者应用在后台被恶意唤醒执行了非预期操作?这些安全风险很可能源于URL Scheme(统一资源定位符模式)的滥用。作为Android组件化开发的核心框架,ARouter虽然提供了便捷的页面跳转能力,但默认配置下仍存在被攻击的风险。本文将通过3个实战步骤,教你如何加固ARouter路由安全,彻底堵上URL Scheme攻击的漏洞。
URL Scheme攻击原理:3行代码就能打开你的App
URL Scheme是Android系统中用于跨应用通信的协议,就像网页中的HTTP链接一样,应用可以通过自定义Scheme(如arouter://m.aliyun.com/test)被外部调用。但这项便捷功能也成为了攻击者的突破口。
攻击者只需构造如下恶意链接:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("arouter://m.aliyun.com/pay?amount=100&to=attacker"));
startActivity(intent);
如果你的App没有适当防护,这段代码就能直接打开支付页面并自动执行转账操作。更危险的是,攻击者可以通过社交软件、网页广告等渠道诱导用户点击,实现"一键攻击"。
ARouter框架通过SchemeFilterActivity处理外部URL跳转请求,其核心代码位于app/src/main/java/com/alibaba/android/arouter/demo/SchemeFilterActivity.java。但默认实现中仅做了基础转发,缺乏严格的安全校验:
// 默认实现仅直接转发URL,存在安全隐患
Uri uri = getIntent().getData();
ARouter.getInstance().build(uri).navigation(this, new NavCallback() {
@Override
public void onArrival(Postcard postcard) {
finish();
}
});
防护机制解析:ARouter的安全基因
ARouter在设计时已考虑到基本的安全需求,通过分层防护机制抵御常见攻击:
1. Scheme注册白名单
在AndroidManifest.xml中,ARouter通过 声明支持的Scheme和Host,限制了可被唤起的协议类型。项目中的配置如下: app/src/main/AndroidManifest.xml
<intent-filter>
<data
android:host="m.aliyun.com"
android:scheme="arouter"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
这种配置确保只有arouter://m.aliyun.com开头的URL才能唤起应用,初步过滤了非法Scheme。
2. 路由拦截器机制
ARouter提供了拦截器功能,可在跳转前对路由请求进行校验。通过实现IInterceptor接口,开发者可以添加登录检查、权限验证等安全逻辑。典型的拦截器实现如下:
@Interceptor(priority = 8, name = "安全检查拦截器")
public class SecurityInterceptor implements IInterceptor {
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
if (isValid(postcard.getUri())) {
callback.onContinue(postcard); // 验证通过,继续跳转
} else {
callback.onInterrupt(new SecurityException("非法路由请求")); // 拦截非法请求
}
}
@Override
public void init(Context context) {}
}
拦截器的优先级机制(priority)确保安全检查会在业务逻辑前执行,形成第一道防线。
3. 自动注册与代码混淆
ARouter通过编译时注解生成路由表,避免了运行时反射带来的安全风险。同时官方文档推荐了严格的混淆规则,防止路由信息泄露: README_CN.md
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
这些规则确保路由框架核心类不被混淆,同时保护业务页面的路由信息不被逆向工程获取。
安全加固实战:3步打造铜墙铁壁
步骤1:强化SchemeFilterActivity过滤逻辑
默认的SchemeFilterActivity仅做简单转发,我们需要增强其安全校验能力。修改app/src/main/java/com/alibaba/android/arouter/demo/SchemeFilterActivity.java,添加来源校验和参数过滤:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = getIntent().getData();
if (isSafeUri(uri)) { // 新增安全校验
ARouter.getInstance().build(uri).navigation(this, new NavCallback() {
@Override
public void onArrival(Postcard postcard) {
finish();
}
@Override
public void onInterrupt(Throwable ex) {
// 拦截处理,跳转到安全页面
ARouter.getInstance().build("/error/403").navigation();
finish();
}
});
} else {
// 非法请求处理
finish();
}
}
// 新增安全校验方法
private boolean isSafeUri(Uri uri) {
if (uri == null) return false;
// 1. 验证Host白名单
String host = uri.getHost();
if (!"m.aliyun.com".equals(host)) {
return false;
}
// 2. 验证来源应用
String callingPackage = getCallingPackage();
if (!isTrustedApp(callingPackage)) {
return false;
}
// 3. 过滤敏感参数
Set<String> queryNames = uri.getQueryParameterNames();
for (String name : queryNames) {
if (name.contains("password") || name.contains("token")) {
return false;
}
}
return true;
}
步骤2:实现签名验证拦截器
创建一个全局拦截器,对所有路由请求进行应用签名校验,确保只有受信任的应用才能调用敏感接口。在module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testinterceptor/Test1Interceptor.java中添加:
@Interceptor(priority = 10, name = "签名验证拦截器")
public class SignatureCheckInterceptor implements IInterceptor {
private Context mContext;
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
// 仅对外部URL路由进行校验
if (postcard.getExtra() == Consts.ROUTE_FROM_EXTERNAL) {
String callingPkg = postcard.getExtras().getString("callingPackage");
if (TextUtils.isEmpty(callingPkg) || !verifySignature(callingPkg)) {
callback.onInterrupt(new SecurityException("应用签名验证失败"));
return;
}
}
callback.onContinue(postcard);
}
// 验证调用应用的签名
private boolean verifySignature(String packageName) {
try {
PackageInfo pkgInfo = mContext.getPackageManager()
.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
// 将签名转换为字符串进行比较
String signature = signatureToString(pkgInfo.signatures[0]);
// 对比预存的可信签名(实际项目中建议使用更安全的校验方式)
return "trusted_signature_123456".equals(signature);
} catch (Exception e) {
return false;
}
}
@Override
public void init(Context context) {
mContext = context;
}
}
步骤3:敏感页面添加二次验证
对于支付、个人信息等敏感页面,即使通过了前面的校验,仍需添加二次确认机制。在目标Activity的onCreate方法中添加:
@Route(path = "/user/profile")
public class ProfileActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ARouter.getInstance().inject(this);
// 检查是否来自外部路由
if (getIntent().getBooleanExtra("from_external", false)) {
// 显示二次确认对话框
new AlertDialog.Builder(this)
.setTitle("安全验证")
.setMessage("检测到外部请求,是否继续?")
.setPositiveButton("确认", (dialog, which) -> loadData())
.setNegativeButton("取消", (dialog, which) -> finish())
.show();
} else {
loadData();
}
}
}
攻击防护效果演示
完成以上加固后,我们来对比防护前后的效果差异。正常情况下,合法的URL跳转流程如下:
当遇到恶意URL攻击时,加固后的应用会呈现以下防护状态:
- 拦截非法Scheme:自动拒绝非
arouter://协议的请求 - 拦截未知来源:对未在白名单中的调用应用直接阻断
- 敏感操作验证:支付、转账等操作需要二次确认
- 异常日志记录:所有拦截事件会记录到安全日志,便于后续审计
安全配置检查清单
为确保加固措施全面生效,部署前请对照以下清单进行检查:
| 检查项目 | 配置位置 | 安全要求 |
|---|---|---|
| Scheme白名单 | AndroidManifest.xml | 仅注册必要的scheme和host |
| 签名验证 | SignatureCheckInterceptor.java | 保存正确的签名哈希值 |
| 参数过滤 | SchemeFilterActivity.java | 移除password/token等敏感参数 |
| 敏感页面保护 | 目标Activity | 添加二次确认或登录验证 |
| 混淆配置 | proguard-rules.pro | 确保路由类不被混淆 |
| 日志审计 | 拦截器实现 | 记录所有异常路由请求 |
总结与进阶
通过本文介绍的3个步骤,你已经为ARouter路由系统构建了多层防护体系。但安全是一个持续过程,建议定期关注ARouter官方更新和安全公告:
- 官方文档:README_CN.md
- 安全相关源码:arouter-api/src/main/java/com/alibaba/android/arouter/core/InterceptorServiceImpl.java
- 社区交流:

记住,安全加固没有银弹,只有通过"默认拒绝+最小权限+深度防御"的原则,结合业务场景持续优化,才能真正抵御日益复杂的攻击手段。
最后,推荐实现ARouter的DegradeService接口,为所有未匹配的路由请求提供统一的降级处理,避免向攻击者泄露系统信息:
@Route(path = "/degrade/default")
public class DefaultDegradeServiceImpl implements DegradeService {
@Override
public void onLost(Context context, Postcard postcard) {
// 跳转到统一错误页面
ARouter.getInstance().build("/error/404").navigation();
}
@Override
public void init(Context context) {}
}
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




