如何阻止第三方应用恶意启动已导出的Activity以避免应用受干扰?
这种情况确实挺闹心的——为了支持Custom Tab的回调跳转,不得不把Activity设为exported="true",结果被别有用心的第三方应用钻空子反复启动,直接搞崩了正常使用。别担心,我给你几个靠谱的解决思路,既能保住Custom Tab的正常功能,又能挡住恶意启动:
第一招:给Intent加“身份验证”,只认合法请求
你的Activity本来就是用来处理Custom Tab的重定向请求的,这类请求必然带有特定的ACTION_VIEW动作和符合规则的data(也就是你在intent-filter里定义的scheme、host、pathPrefix)。那我们就可以在Activity的启动逻辑里加一道校验:只要不是符合要求的Intent,直接关门送客。你可以在
MyActivity的onCreate方法开头加上这段校验代码:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent incomingIntent = getIntent(); // 先检查Intent的动作和数据是否合法 if (incomingIntent == null || !Intent.ACTION_VIEW.equals(incomingIntent.getAction()) || incomingIntent.getData() == null) { finish(); return; } // 再精准校验data的scheme、host和路径前缀 Uri intentData = incomingIntent.getData(); if (!"myapp".equals(intentData.getScheme()) || !"my.host".equals(intentData.getHost()) || !intentData.getPath().startsWith("/mypage")) { finish(); return; } // 到这里才是合法请求,继续执行正常业务逻辑 }这样一来,第三方用
ComponentName直接启动的Intent因为没有ACTION_VIEW和合法的data,一启动就会被直接关闭,根本走不到正常业务逻辑,自然也就干扰不到你的应用了。第二招:校验调用者身份,只放行可信来源
除了校验Intent本身,你还可以检查是谁启动了这个Activity。毕竟Custom Tab的重定向请求都是由浏览器类应用发起的,而恶意应用的包名肯定不在你的信任列表里。你可以在
MyActivity里加一段调用者校验的代码:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String callerPackage = getCallingPackage(); // 拿不到调用者信息的直接拒绝 if (callerPackage == null) { finish(); return; } // 定义你的信任包名列表:自己的应用包名+常用浏览器包名 Set<String> trustedPackages = new HashSet<>(); trustedPackages.add("my.host"); // 你自己的应用包名 trustedPackages.add("com.android.chrome"); // Chrome浏览器 trustedPackages.add("com.google.android.webview"); // 系统WebView // 可以根据需求添加其他主流浏览器的包名 if (!trustedPackages.contains(callerPackage)) { finish(); return; } // 剩下的正常业务逻辑 }这里要注意,
getCallingPackage()在少数特殊场景下可能返回null,所以一定要做判空处理,避免出现意外问题。第三招:用启动模式缓解干扰(可选,配合前两招效果更好)
如果你担心极端情况下还是有漏网之鱼,可以给这个Activity设置android:launchMode="singleTask"(在AndroidManifest里)。这样即使第三方多次启动,系统也只会保留一个Activity实例,不会无限创建新页面,能最大程度降低应用崩溃的风险。不过这只是个辅助手段,核心还是前面的身份校验。调整后的Activity配置大概是这样:
<activity android:name="my.host.MyActivity" android:exported="true" android:launchMode="singleTask"> <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:host="my.host" android:pathPrefix="/mypage" android:scheme="myapp" /> </intent-filter> </activity>
一般来说,把第一招和第二招结合起来用,就能形成双重保险,既不影响Custom Tab的正常跳转,又能把恶意启动彻底挡在门外。
内容来源于stack exchange




