如何用移动应用确保通信的安全性(真实性、隐私与完整性)?

34 投票
3 回答
7032 浏览
提问于 2025-04-17 09:54

一个安卓或苹果的应用程序需要从服务器获取应用数据。

我该如何确保与移动应用之间的通信安全呢?

期望:要足够安全,以保护像密码这样的敏感信息,除了暴力破解外,不能有直接的解密方式。

我的要求

  • 认证 [只有这个应用被授权]
  • 完整性 [信息在传输过程中不能被修改]
  • 隐私 [如果有人监听,通信内容不能被读取]

我的努力

  • SSL 只对服务器进行认证,而不对客户端进行认证。
  • 我不能使用对称加密 [只能提供隐私保护]
  • 数字签名不行 [缺乏隐私保护]
  • PGP 满足这三个要求。

问题

  • PGP 需要在客户端应用上存储密钥。
  • 似乎没有可靠的方法来保护客户端应用上的密钥。
  • 如果密钥被泄露,那么 PGP 和对称加密都是一样脆弱的。
  • 反向工程破解 PGP 密钥或对称密钥同样困难。
  • 在这种情况下,PGP 对移动处理器来说就是一种无意义的负担。
  • OAuth 也没用,因为它同样有一个客户端密钥。

那么,我该如何继续进行呢?行业是如何处理这个问题的?

我是否应该采取简单的做法:

  • 使用简单的 SSL,然后祈祷一切顺利? 因为如果密钥被盗,认证就不可能了?(这样只能对服务器进行认证)

更新:

最后的结论是使用 AES,因为如果我能保持密钥的安全,那我就和 SSL 一样好。而且我可以随着时间的推移不断更换密钥以提高安全性。如果你认为有更好的方法,请贡献你的想法,发帖前请先阅读完整内容。

3 个回答

-1

可以使用SSL进行客户端认证,或者在服务器认证的SSL上再加一层自己的客户端认证(比如用户名/密码、令牌等)。

(编辑:把评论移到这里,因为作为评论放不下)

再详细说一下,任何认证信息都需要在应用里存储或输入。如果让用户每次都输入密码,那就不需要保存密码,但这样显然不太方便。你可以用设备特定的密钥来加密密码,这样在被破解的设备上就看不到密码。对于私钥,你要么用用户输入的密码来保护它(就像上面说的),要么让系统来保护它。这个功能从Android 4.0(冰淇淋三明治)开始提供,使用的是系统密钥库的公共API,也就是KeyChain类。在这种情况下,用户需要解锁手机(用图案、密码或PIN码)才能访问密钥库。

5

SSL确实有双向认证,正如其他评论者提到的那样。但我认为你不应该尝试去认证客户端,也就是应用程序。你只需要认证用户(在OAuth术语中称为资源拥有者),而不是代理或客户端。

手机应用程序无法保存任何秘密,所以绝对不要把证书或密码放在设备上。一个典型的坏例子是把用户名和密码保存在某个系统的密钥库里,比如iOS的钥匙串。如果应用用户没有给手机设置密码,那么整个密钥库就会以明文形式保存,任何人都可以轻易获取所有信息。把证书嵌入到应用中几乎和把秘密放在手机里一样糟糕,因为手机不像服务器那样被锁在计算机房里,人们常常会丢失手机。

基于这个原因,你需要一个基于令牌的解决方案,这样应用就不需要保存秘密。你可以传递秘密(用户登录凭证),然后立即清除内存中的这些信息。你只需要保存一个令牌,这个令牌的有效期很短(比如30分钟后过期)。

OAuth 2.0的隐式流就是为了解决这个问题而设计的。不过,它并不完美,并且OAuth 2.0规范中存在一些基本问题。特别是,实施这个流程需要应用使用UIWebView(嵌入式浏览器),而这本身可能不安全,用户体验也不好。因此,这几乎排除了所有基于重定向的流程。唯一一个广为人知使用OAuth 2重定向流程的应用是Facebook,但它的实现也很糟糕。

OAuth 2.0资源拥有者流程是一个选项。通过这个流程,你整个系统的安全级别可以和B2C解决方案一样高——比如基于浏览器的在线银行解决方案。这意味着任何拥有用户名和密码的人都能访问服务器上的资源——这和基于浏览器的解决方案的安全级别是一样的。

不过,你仍然需要小心,正如之前提到的,OAuth 2规范中存在一些基本问题——在这种情况下,你不能按照规范来实现令牌刷新逻辑——这通常涉及使用一个永不过期的刷新令牌——这可以在谷歌的OAuth 2实现中看到。这个令牌就成了一个秘密——这违背了使用OAuth的初衷。

一个解决方法是根据最后的活动自动续订令牌。

总之,移动应用安全并不是一个新话题,但遗憾的是我们仍然没有任何标准工具或机制来解决这些独特的挑战。

这就是为什么银行愿意花费数百万来开发他们的移动银行解决方案,但仍然会失败的原因(http://hothardware.com/News/Mobile-Banking-Apps-for-iOS-Vulnerable-to-Man-in-the-Middle-Attacks/);-)

23

你现在得到的信息不太准确。SSL确实可以用来验证客户端,只是大多数情况下并没有这样做。因为这个协议最初主要是为了保护电子商务网站,确保服务器的身份是重要的,而客户端的身份验证则不那么重要或者不太可行。你需要做的是使用双向认证的SSL,这样你的服务器只会接受来自你的应用的连接,而你的应用也只会和你的服务器进行通信。

这里有个大致的步骤。首先,创建一个自签名的服务器SSL证书,并把它部署在你的网络服务器上。如果你在使用Android,可以用Android SDK自带的keytool来完成这个;如果你用的是其他平台,比如iOS,也有类似的工具。接着,创建一个自签名的客户端证书,并把它放在你的应用中,作为一个资源存放在自定义的密钥库里(keytool也会生成这个)。然后,配置服务器要求客户端进行SSL认证,并只接受你生成的客户端证书。再配置客户端,使用这个客户端证书来识别自己,并只接受你在服务器上安装的那个服务器证书。

如果有其他人或其他东西试图连接你的服务器,SSL连接就不会建立,因为服务器会拒绝那些没有提供你在应用中包含的客户端证书的连接。

详细的步骤会比这里所需的要长得多。我建议你分阶段进行,因为网上有很多资源可以帮助你处理Android和iOS上自签名SSL证书的相关问题,包括服务器和客户端的部分。我的书《Android平台的应用安全》中也有完整的操作指南,出版方是O'Reilly。

撰写回答