应用程序应如何存储其凭据

信息安全 爪哇 贮存 证书
2021-08-26 19:28:20

语境

在开发桌面应用程序时,您有时必须将凭据存储在某个地方才能对您的应用程序进行身份验证。一个例子是Facebook 应用 ID + secret,另一个是 MySQL 凭证。

将这些纯文本存储在应用程序源代码中并不能提供任何真正的安全性,因为对程序进行逆向工程并不麻烦从服务器收集凭据也无济于事,因为黑客很容易自己执行请求。存储凭证的加密也不会产生任何影响,因为应用程序需要访问解密密钥才能使用凭证。


问题

如何安全地存储特定于应用程序的凭据?最好是跨操作系统。

注意: 相关语言是 Java,但是,我相信(认为)这是一个与语言无关的问题。

2个回答

切勿在程序中硬编码密码或加密密钥。

一般的经验法则是:您应该存储在用户计算机上的唯一凭据是与该用户关联的凭据,例如,使该用户能够登录到他/她的帐户的凭据。

您不应将开发人员凭据存储在用户的机器上。那不安全。

您必须假设存储在用户机器上的任何内容都为用户所知,或者用户可以轻松学习。(这是正确的假设:对应用程序二进制文件进行逆向工程以了解可能嵌入其中的任何密钥或秘密并不难。)

一旦你理解了这个一般原则,一切都会变得容易。基本上,然后您需要设计系统的其余部分和身份验证协议,以便客户端软件可以仅使用可以安全存储在客户端上的那些凭据进行身份验证。

示例 1. 假设您有一个 Facebook 应用程序 ID 和密钥,与您的应用程序相关联(即与您的开发人员帐户相关联)。您是否将应用程序 ID 和密钥嵌入到您交付给用户的桌面软件中?不!绝对不。您绝对不要这样做,因为这会让您的任何用户了解您的应用 ID 和密钥并提交他们自己的请求,这可能会损害您的声誉。

相反,你会找到另一种方式。例如,也许您设置了自己的服务器,该服务器具有应用程序 ID 和密钥,并负责向 Facebook 平台发出请求(受到适当的限制和速率限制)。然后,您的客户端连接到您的服务器。也许您通过让每个用户在您的服务器上设置他/她自己的用户帐户、将帐户凭据存储在客户端上并让客户端使用这些凭据对自己进行身份验证来验证每个客户端。

您可以通过让客户端应用程序在第一次执行时生成一个新的用户帐户(生成自己的登录凭据,将它们存储在本地,并将它们发送到服务器)来使用户完全不可见。客户端应用程序可以使用这些存储的凭据在将来进行连接(例如,通过 SSL),并在以后每次执行应用程序时自动登录。

请注意,存储在用户计算机上的唯一内容是允许登录该用户帐户的凭据——但没有任何内容允许登录其他人的帐户,也没有任何内容会暴露开发人员应用程序密钥。

示例 2。假设您编写的应用程序需要访问用户 Google 帐户中的数据。您是否提示他们输入 Google 用户名和密码并将其存储在应用程序本地存储中?你可以:那没关系,因为用户的凭据存储在用户的机器上。用户没有动力尝试破解自己的机器,因为他们已经知道自己的凭据。

更好的是:使用 OAuth 授权您的应用程序。这样,您的应用程序会将 OAuth 令牌存储在其应用程序本地存储中,从而允许您的应用程序访问用户的 Google 帐户。它还避免了将用户的 Google 密码(特别敏感)存储在应用程序的本地存储中,从而降低了泄露风险。

示例 3。假设您正在编写一个应用程序,该应用程序具有一个在所有用户之间共享的 MySQL 数据库后端。您是否使用 MySQL 数据库并将其嵌入到应用程序二进制文件中?不!您的任何用户都可以提取密码,然后直接访问您的 MySQL 数据库。

相反,您设置了一个提供必要功能的服务。客户端应用程序连接到服务,对自身进行身份验证,并将请求发送到服务。然后该服务可以在 MySQL 数据库上执行此请求。MySQL 密码安全地存储在服务器的机器上,并且永远无法在任何用户的机器上访问。服务器可以施加您想要的任何限制或访问控制。

这要求您的客户端应用程序能够对服务进行身份验证。一种方法是让客户端应用程序在首次运行时在服务上创建一个新帐户,生成随机身份验证凭据,并在以后每次自动登录服务。您可以使用带有随机密码的 SSL,或者更好的是,使用 SSL 之类的东西,为每个客户端使用唯一的客户端证书。


另一条规则是:您不要将凭据硬编码到程序中。如果您将凭据存储在用户的机器上,请将它们存储在某个私有位置:可能是配置文件或目录,最好是仅可由该特定应用程序或该特定用户读取的目录(不是世界可读的文件)。

这是一个经典的安全问题,没有完美的解决方案,只有不完美的解决方案,它归结为保护软件免受篡改和逆向工程的更普遍的问题。

  1. 使用用户必须主动提供以获取凭据的外部身份验证方法:手动输入的密码(例如,其哈希摘要用于解密凭据)、包含证书和匹配私钥的安全身份验证加密狗输入到 USB 端口、提供正确指纹的指纹读取器等。理想情况下,结果不会是对您的程序的简单是/否回答,因为这可以被覆盖/修补/欺骗,而是一个真实值(a解密您的凭据(或您要保护的任何其他内容)所需的密钥),直接从身份验证器派生。从各种来源(至于哪些来源,这实际上取决于您的系统)动态计算解密密钥的多源方法可能会更好。

  2. 严重(自动和大规模)混淆您的程序以阻止逆向工程。的确,静态分析工具已经成为最先进的工具,但是有[专有的、昂贵的]混淆工具(混淆编译器、打包器等)使逆向工程非常耗时、具有挑战性和费力,足以让攻击者寻找更容易的目标。添加针对调试和防篡改方法的保护机制可以进一步增强程序的安全性。确实,Java 作为一种字节码语言在这方面特别容易受到攻击,因为反编译它(与反编译/反汇编本机代码相比)相当简单。