3步搞定ARouter路由安全:从URL Scheme漏洞到攻防实战

3步搞定ARouter路由安全:从URL Scheme漏洞到攻防实战

【免费下载链接】ARouter 💪 A framework for assisting in the renovation of Android componentization (帮助 Android App 进行组件化改造的路由框架) 【免费下载链接】ARouter 项目地址: https://gitcode.com/gh_mirrors/ar/ARouter

你是否遇到过这样的情况:用户点击一条看似正常的链接,却意外打开了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跳转流程如下:

ARouter正常跳转演示

当遇到恶意URL攻击时,加固后的应用会呈现以下防护状态:

  1. 拦截非法Scheme:自动拒绝非arouter://协议的请求
  2. 拦截未知来源:对未在白名单中的调用应用直接阻断
  3. 敏感操作验证:支付、转账等操作需要二次确认
  4. 异常日志记录:所有拦截事件会记录到安全日志,便于后续审计

安全配置检查清单

为确保加固措施全面生效,部署前请对照以下清单进行检查:

检查项目配置位置安全要求
Scheme白名单AndroidManifest.xml仅注册必要的scheme和host
签名验证SignatureCheckInterceptor.java保存正确的签名哈希值
参数过滤SchemeFilterActivity.java移除password/token等敏感参数
敏感页面保护目标Activity添加二次确认或登录验证
混淆配置proguard-rules.pro确保路由类不被混淆
日志审计拦截器实现记录所有异常路由请求

总结与进阶

通过本文介绍的3个步骤,你已经为ARouter路由系统构建了多层防护体系。但安全是一个持续过程,建议定期关注ARouter官方更新和安全公告:

记住,安全加固没有银弹,只有通过"默认拒绝+最小权限+深度防御"的原则,结合业务场景持续优化,才能真正抵御日益复杂的攻击手段。

最后,推荐实现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) {}
}

【免费下载链接】ARouter 💪 A framework for assisting in the renovation of Android componentization (帮助 Android App 进行组件化改造的路由框架) 【免费下载链接】ARouter 项目地址: https://gitcode.com/gh_mirrors/ar/ARouter

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值