【问题标题】:How to validate the APK signing certificate at client side?如何在客户端验证 APK 签名证书?
【发布时间】:2019-06-02 20:25:49
【问题描述】:

我正在开发一个与服务器通信的 android 应用程序,我想在运行时验证我的应用程序自发布以来没有被修改(使用修改后的应用程序的用户应该无法登录到应用程序)。

由于修改后的应用程序签名与原始签名不同,我决定:

  1. 提取嵌入在 android 应用程序中的签名证书
  2. 将其与登录请求一起发送到服务器(在客户端(即 apk)中编码整个验证过程不起作用,因为有人可以修改 apk 以绕过它。)
  3. 验证一下
  4. 如果证书有效,则处理登录请求/否则返回错误

这是我上面数字 1 的代码:

Context context = this;
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;

try {
    packageInfo = pm.getPackageInfo(packageName, flags);
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
}

Signature[] signatures = packageInfo.signatures;  

所以我的问题是:

  1. 是否有更好的方法来验证 apk?
  2. 如果这个方法没问题,
    2.1 发送证书会不会占用很高的带宽?
    2.2 如何验证证书?(我在服务器端有mykey.jks,我最初用于签署apk)

(这也是我关于stackoverflow的第一个问题,因此非常感谢指出我在提出问题时所犯的任何错误!。)

【问题讨论】:

  • 要么你可以检查你的应用程序。是否从游戏商店下载。就是这样。
  • 这也很好,但问题是,它完全依赖于客户端,因此可以相对容易地绕过它。但它也不会伤害到那个级别的检查。

标签: android security certificate client-server apk


【解决方案1】:

首先,有人总是有可能绕过这种安全措施,例如通过硬编码传递给您的服务器的证书。您只能尝试使其尽可能难,但它永远不会 100% 安全。

话虽如此,你所做的似乎还可以。请注意,在最新的 SDK 中,pm.signatures 已弃用,取而代之的是 pm.signingInfo

在大小方面,证书并不大,但要使请求更小,您可能应该只发送一个哈希值(例如 SHA256)。

为了防止人们在请求中对证书进行硬编码,您还可以考虑将另一个值与证书一起哈希,即哈希(证书 + 时间戳),并在请求中发送时间戳,以便您可以重新计算哈希服务器端.如果时间戳与当前日期相差太远,则拒绝该请求。同样,它也不是完全安全的,但会增加另一层复杂性来对代码进行逆向工程。您还可以添加 versionCode 并开始拒绝旧版本的请求(例如,如果您在其中一个旧版本中检测到安全漏洞)并提示这些用户更新应用程序(或在后台为他们执行此操作,并且只提示输入安装,感谢新的 API Play 提供)。

正如有人指出的那样,您的代码还可以检查出安装来自 Play (packageManager.getInstallerPackageName(getPackageName()).equals("com.android.vending");),但该信息很容易被欺骗,因此不确定它是否会增加安全性。

希望对你有帮助,

【讨论】:

  • 是的,我在寻找一种完美(至少几乎完美)的方法。估计我太天真了。但无论如何,我想添加多层安全检查并没有什么坏处。附言感谢您更新 SDK 和 API 以及时间戳方法。我一定会考虑的。
  • 发布前可以在服务器上保存发布时间戳
【解决方案2】:

正如@Pierre 已经指出的那样,您执行检查的方式是有效的,但我想提醒您可以在运行时绕过证书固定这一事实,使用诸如 FridaxPosed 之类的工具框架,但仍建议并鼓励将其用作另一个安全层。有关这方面的更多见解,请快速阅读this article,以了解如何轻松实施固定,从运营角度维护它如何成为一场噩梦以及如何绕过它。

有没有更好的方法来验证 apk?

是的,它存在并且被称为移动应用证明。该解决方案将包含一个集成在移动应用程序中的 SDK,该 SDK 在后台与云服务进行通信,因此不会影响用户体验。

移动应用程序证明解决方案可在运行时保证应用程序不会受到中间人攻击、被篡改、不会在有根或越狱的设备中运行、不会连接到调试器、不会在模拟器上运行和上传到应用商店或谷歌游戏商店的原版是一样的。

因此,成功证明移动应用程序完整性的云服务会发出一个非常短暂的JWT token,该JWT token 使用只有 API 服务器和云中运行的移动应用程序证明服务知道的秘密签名。在证明失败时,JWT 使用 API 服务器不知道的秘密进行签名。在对 API 服务器的每个请求中,移动应用都会发送此 JWT 令牌,API 服务器将验证签名是否有效且令牌尚未过期,并在任何验证失败时拒绝该请求。

一旦移动应用不知道云证明服务使用的秘密,就不可能对 JWT 令牌进行逆向工程,即使移动应用被篡改、在有根设备中运行或通过成为中间人攻击的目标。

您可以在Approov(我在这里工作)中找到这样的服务,它具有适用于包括 Android 在内的多个平台的 SDK。集成还需要对 API 服务器代码进行小检查以验证 JWT 令牌。

Frida

面向开发人员、逆向工程师和安全研究人员的动态检测工具包。

xPosed

Xposed 是一个模块框架,可以在不触及任何 APK 的情况下改变系统和应用的行为。

JWT Token

基于令牌的身份验证

JSON Web 令牌是一种开放的行业标准 RFC 7519 方法,用于在两方之间安全地表示声明。

【讨论】:

  • 其实我是在网络安全领域工作的,在研究移动应用相关的安全时,我遇到了Approov。老实说,我并没有完全理解它是如何工作的,以及它与通常(前面提到的)方法的区别。如果您可以发布以下问题的答案 - 它如何检查应用程序的完整性 - 如何无法绕过根/完整性检查,那就太好了。
  • 我们不执行检查,因为我们在设备中做出决定,我们采取了一些我们发送回云服务的测量来确定它是否发出有效的签名令牌并且这个令牌是不可能的在确定它们是有效还是无效以及伪造它们的意义上进行逆向工程。正如您可能理解的那样,我们无法详细说明,一旦这是我们收据的秘诀,但对于我们的客户,我们可以根据保密协议披露更多信息。请随时直接与我们联系或在 Stackoverflow 中标记为#approov 的问题。
  • 是的,我完全理解。感谢您的信息。我一定会与#approov 保持联系。
  • 很高兴我能提供帮助。很快再见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-07
  • 2018-09-21
  • 2013-07-14
  • 2022-01-07
  • 1970-01-01
  • 2018-10-15
  • 1970-01-01
相关资源
最近更新 更多