大家好,今天我想向您介绍一些在两个android应用程序之间传输数据的选项,并从安全角度考虑它们。 我决定写这篇文章有两个原因。 首先,我经常开始缺乏对开发人员使用Android应用程序组件的机制的了解。 第二个-我停止理解在实现功能时对这种机制的选择基于什么,而是想传达它应该如何看待最低要求。
挑战赛
我们有2个访问相同API的应用程序。 客户端可以通过访问令牌(sessionId)访问API。 您必须实现从一个应用程序到另一个应用程序的无缝过渡。 为此,您需要在它们之间摸索,例如,将其设为sessionId。
选项1:QUERY DEEPLINK
最明显的选择是将令牌传输到查询DeepLink。 它看起来像这样:
slave://main?sessionId=686A885A4FB644053C584B9BE2A70C7D
在这种情况下,接收者将能够提取sessionId并使用它,而无需请求用户授权。 从开发人员的角度看,任务似乎已经完成,但让我们进行更深入的研究。
深度链接劫持
由于任何应用程序都可以注册tinkoff://方案,因此OS可以打开错误的应用程序。 由于没有注册和使用方案的限制这一事实,所以这是可能的。 恶意应用程序可能会注册tinkoff://方案,并拦截对Tinkoff应用程序的请求并自行启动。 在这种情况下,sessionId将落入错误的手中,您的帐户也将受到损害。 此外,DeepLink Hijacking允许您进行网络钓鱼,例如,显示用于输入用户名和密码的字段。
从概念上讲,该过程如下所示:

有2个解决方案。 首先,AppLinks技术不再允许开发人员自定义方案;而是使用http / https。 在这种情况下,操作系统将采用
slave.com/profile链接并与slave.com主机联系以进行验证。 第二个-Intent URL-而不是调用slave://,而是调用intent://,在此传递要启动的应用程序的唯一标识符。 看起来像这样:
intent://main/#Intent;scheme=slave;package=com.example.slave.client.android;end"
在这种情况下,由于指定了特定的程序包,因此无法拦截应用程序的启动。 尽管如此,问题仍然在于用户可以从第三方源以与您的从属相同的packageId安装应用程序。 在这种情况下,如果您没有合法的从属应用程序,则恶意应用程序将安装并接收您的令牌。
会话固定
这是一种攻击,攻击者迫使客户端使用攻击者提供的sessionId与目标软件建立会话。 一旦用户进行身份验证,攻击者便可以将这个已经特权的标识符用于自己的目的。 该攻击利用了特权升级后目标软件使用相同的sessionId的事实。

在我们的情况下看起来如何:
- 攻击者从应用程序获取匿名会话
- 代表银行向受害人扔了一封精美的信,邀请受害人进入他的个人帐户
- 单击链接时,我们进入带有攻击者会话从站的DeepLink:// main?sessionId = 686A885A4FB644053C584B9BE2A70C7D
- 移动应用程序进行会话,了解它没有足够的权限,并要求用户进行身份验证
- 用户通过它,会话具有增加的权限
- 应用程序中的用户,具有特权会话的攻击者,获利
将其修复在API上是正确的,在特权升级后发出另一个sessionId,但是我们正在编写一个移动应用程序。 我们的方法是拒绝将令牌从主机转移到从机。 另外,这将为我们提供深入的保护,并且,如果API发生故障,并且特权增加时令牌也不会更改,则仍然无法进行攻击。
第三方泄漏
此选项的另一个缺点。 由于生成链接,分析和其他有趣内容的便利,许多人将第三方服务用于DeepLink。 在这种情况下,您只需将令牌提供给第三方公司。
选项2:内容提供商
我们将如何做? 我们定义主Content-Provider,并使从属转到该Content-Provider获得令牌。

因此,在DeepLink劫持的情况下,我们避免了将令牌转移到错误的应用程序的风险,并使会话固定攻击无法进行。 但是我们还有其他问题-在当前版本中,一般来说,即使我们没有启动启动程序,任何应用程序都可以随时请求令牌。
防护等级
在大多数情况下,您需要验证从属服务器是否使用与主服务器相同的密钥签名,即它们属于同一作者。 对于这种情况,程序包管理器具有一个checkSignatures方法,用于检查应用程序签名。 要使用此功能,您需要在应用程序清单的Content-Provider中添加具有protectionLevel =“ signature”的权限:
<permission android:name="com.example.contentprovider.access" android:protectionLevel="signature"/> <application> <provider ... android:readPermission="com.example.contentprovider.access"> </application>
该方案与之前的图几乎没有什么不同,只有一个保证,即只有具有相同作者签名的应用程序才能访问令牌。
许可竞赛条件
权限名称不是唯一的,这是一个非常不愉快的功能,恶意应用程序可以使用该权限名称,并在我们之前使用我们的名称和protectionLevel =“ normal”注册权限。 在这种情况下,安装我们的应用程序时,权限将已经存在于操作系统上,并且不会被覆盖。 因此,我们的内容提供商将不受任何保护,并可以从任何应用程序获得授权访问。
不同的签名
不幸的是,应用程序并不总是使用相同的密钥签名,例如,某些应用程序是购买的,或者“从历史上讲”,但是仍然需要无缝过渡。 在这种情况下,我们自己进行签名验证。
如何实现:
Content-Provider有一个getCallingPackage()方法,通过它我们可以获取已申请数据的应用程序的packageId,通过packageId我们可以获取签名列表并使用内置签名进行检查。
String pkg = this.getCallingPackage(); PackageInfo pkgInfo = pkgmgr.getPackageInfo(pkg, GET_SIGNATURES); Signatures[] signatures = pkgInfo.signatures; for (Signature sig: signatures) { if (sig.equals(TRUSTED_SIGNATURE)) {

看来我们所做的一切都很完美,但是没有。
假身份证漏洞
问题在于,当Android创建信任链时,验证过程只会比较主题,而不会验证证书的签署者字段中的签名。 结果,攻击者可以在没有实际签名的情况下建立信任链。
由于此错误,将生成不正确的证书链,其中可能包括嵌入在APK中但未真正用于对应用程序进行签名的合法证书。 最后,我将留下指向修复此漏洞的提交的链接。 该问题已在android 4.4中修复,因此我们只能将API级别提高到19。
结论
今天,我们研究了在开发过程中应如何分析功能。
我们还研究了在两个应用程序之间传输秘密的选项,在此期间,我们分析了每个选项的问题并提出了避免这些问题的方法。
所有安全的应用程序!
参考文献