Java .class文件可以很容易地反编译。如果必须在代码中使用登录数据,如何保护数据库?
.class
切勿将密码硬编码到您的代码中。最近在最危险的25个编程错误中提到了这一点:
将机密帐户和密码硬编码到您的软件中非常方便- 对于熟练的反向工程师。如果所有软件上的密码都相同,那么当不可避免地知道该密码时,每个客户都会受到攻击。而且由于它是硬编码的,因此修复起来非常痛苦。
您应将配置信息(包括密码)存储在应用程序启动时读取的单独文件中。这是防止密码因反编译而泄漏的唯一真实方法(切勿首先将其编译为二进制文件)。
有关此常见错误的更多信息,您可以阅读CWE-259文章。本文包含更详尽的定义,示例以及有关此问题的许多其他信息。
在Java中,最简单的方法之一是使用Preferences类。它旨在存储各种程序设置,其中一些可能包括用户名和密码。
import java.util.prefs.Preferences; public class DemoApplication { Preferences preferences = Preferences.userNodeForPackage(DemoApplication.class); public void setCredentials(String username, String password) { preferences.put("db_username", username); preferences.put("db_password", password); } public String getUsername() { return preferences.get("db_username", null); } public String getPassword() { return preferences.get("db_password", null); } // your code here }
在上面的代码中,可以setCredentials在显示询问用户名和密码的对话框后调用该方法。当您需要连接到数据库时,只需使用getUsername和getPassword方法即可检索存储的值。登录凭据不会被硬编码到您的二进制文件中,因此反编译不会带来安全风险。
setCredentials
getUsername
getPassword
重要说明: 首选项文件只是纯文本XML文件。确保您采取了适当的步骤来防止未经授权的用户查看原始文件(UNIX权限,Windows权限等)。至少在Linux中,这不是问题,因为调用Preferences.userNodeForPackage将在当前用户的主目录中创建XML文件,而其他用户还是无法读取该文件。在Windows中,情况可能有所不同。
Preferences.userNodeForPackage
更重要的注意事项: 在此答案的注释以及其他内容的讨论中,有很多讨论是针对这种情况的正确体系结构。最初的问题并没有真正提到应用程序使用的上下文,因此我将讨论我能想到的两种情况。第一种情况是使用该程序的人已经知道(并被授权知道)数据库凭据。第二种情况是您(开发人员)试图对使用该程序的人员保密数据库凭据。
第一种情况:用户被授权知道数据库登录凭据
在这种情况下,我上面提到的解决方案将起作用。Java Preference类将以纯文本形式存储用户名和密码,但是首选项文件仅可由授权用户读取。用户可以简单地打开首选项XML文件并读取登录凭证,但这不会带来安全风险,因为用户知道其开头就是凭证。
Preference
第二种情况:尝试向用户隐藏登录凭据
这是更复杂的情况:用户不应该知道登录凭据,但仍然需要访问数据库。在这种情况下,运行应用程序的用户可以直接访问数据库,这意味着程序需要提前知道登录凭据。我上面提到的解决方案不适用于这种情况。您可以将数据库登录凭据存储在首选项文件中,但是该用户将是所有者,因此该用户将能够读取该文件。实际上,实际上没有很好的方法可以安全地使用此案例。
正确的情况:使用多层体系结构
正确的方法是在数据库服务器和客户端应用程序之间具有中间层,该中间层对单个用户进行身份验证并允许执行一组有限的操作。每个用户都有自己的登录凭据,但没有数据库服务器的登录凭据。凭据将允许访问中间层(业务逻辑层),并且对于每个用户而言都是不同的。
每个用户都有自己的用户名和密码,可以将其用户名和密码本地存储在首选项文件中,而没有任何安全风险。这称为三层体系结构(这些层是数据库服务器,业务逻辑服务器和客户端应用程序)。它比较复杂,但这实际上是执行这种操作的最安全的方法。
操作的基本顺序是:
getInventoryList
请注意,在整个过程中, 客户端应用程序永远不会直接连接到数据库 。业务逻辑层接收来自经过身份验证的用户的请求,处理客户的清单清单请求,然后才执行SQL查询。