如何保护MySQL用户名和密码不被反编译?

发布于 2024-07-11 14:59:21 字数 73 浏览 10 评论 0原文

Java .class 文件可以相当容易地反编译。 如果我必须在代码中使用登录数据,如何保护我的数据库?

Java .class files can be decompiled fairly easily. How can I protect my database if I have to use the login data in the code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

椵侞 2024-07-18 14:59:21

切勿将密码硬编码到您的代码中。 最近在 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 方法。 当您需要连接到数据库时,只需使用 getUsernamegetPassword 方法即可检索存储的值。 登录凭据不会被硬编码到您的二进制文件中,因此反编译不会造成安全风险。

重要提示:首选项文件只是纯文本 XML 文件。 确保采取适当的步骤来防止未经授权的用户查看原始文件(UNIX 权限、Windows 权限等)。 至少在 Linux 中,这不是问题,因为调用 Preferences.userNodeForPackage 会在当前用户的主目录中创建 XML 文件,而其他用户无论如何都无法读取该文件。 在 Windows 中,情况可能有所不同。

更重要的说明:在这个答案和其他人的评论中,关于这种情况的正确架构是什么进行了很多讨论。 原来的问题并没有真正提到应用程序的使用上下文,所以我将谈谈我能想到的两种情况。 第一种情况是使用该程序的人已经知道(并且被授权知道)数据库凭据。 第二种情况是,您(开发人员)试图对使用该程序的人保密数据库凭据。

第一种情况:用户被授权知道数据库登录凭据

在这种情况下,我上面提到的解决方案将起作用。 Java Preference 类将以纯文本形式存储用户名和密码,但首选项文件只能由授权用户读取。 用户只需打开首选项 XML 文件并读取登录凭据,但这并不存在安全风险,因为用户一开始就知道凭据。

第二种情况:尝试向用户隐藏登录凭据

这是更复杂的情况:用户不应该知道登录凭据,但仍然需要访问数据库。 在这种情况下,运行应用程序的用户可以直接访问数据库,这意味着程序需要提前知道登录凭据。 我上面提到的解决方案不适合这种情况。 您可以将数据库登录凭据存储在首选项文件中,但用户将能够读取该文件,因为他们将是所有者。 事实上,确实没有什么好的方法可以安全地使用这种情况。

正确案例:使用多层架构

正确的方法是在数据库服务器和客户端应用程序之间有一个中间层,用于对单个用户进行身份验证并允许一组有限的操作待执行。 每个用户都有自己的登录凭据,但没有数据库服务器的登录凭据。 凭证将允许访问中间层(业务逻辑层),并且对于每个用户来说都是不同的。

每个用户都有自己的用户名和密码,这些用户名和密码可以本地存储在首选项文件中,没有任何安全风险。 这称为三层架构(这些层是您的数据库服务器,业务逻辑服务器和客户端应用程序)。 它更复杂,但它确实是执行此类操作的最安全方法。

操作的基本顺序是:

  1. 客户端使用用户的个人用户名/密码向业务逻辑层进行身份验证。 用户名和密码是用户已知的,并且与数据库登录凭据没有任何关系。
  2. 如果身份验证成功,客户端会向业务逻辑层发出请求,要求从数据库中获取一些信息。 例如,产品库存。 请注意,客户端的请求不是 SQL 查询; 它是一个远程过程调用,例如getInventoryList
  3. 业务逻辑层连接到数据库并检索所请求的信息。 业务逻辑层负责根据用户的请求形成安全的SQL查询。 SQL 查询的任何参数都应该被清理以防止 SQL 注入攻击。
  4. 业务逻辑层将库存列表发送回客户端应用程序。
  5. 客户端向用户显示库存列表。

请注意,在整个过程中,客户端应用程序永远不会直接连接到数据库。 业务逻辑层接收来自经过身份验证的用户的请求,处理客户端对库存列表的请求,然后才执行 SQL 查询。

Never hard-code passwords into your code. This was brought up recently in the Top 25 Most Dangerous Programming Mistakes:

Hard-coding a secret account and
password into your software is
extremely convenient -- for skilled
reverse engineers. If the password is
the same across all your software,
then every customer becomes vulnerable
when that password inevitably becomes
known. And because it's hard-coded,
it's a huge pain to fix.

You should store configuration information, including passwords, in a separate file that the application reads when it starts. That is the only real way to prevent the password from leaking as a result of decompilation (never compile it into the binary to begin with).

For more information about this common mistake, you can read the CWE-259 article. The article contains a more thorough definition, examples, and lots of other information about the problem.

In Java, one of the easiest ways to do this is to use the Preferences class. It is designed to store all sorts of program settings, some of which could include a username and password.

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
}

In the above code, you could call the setCredentials method after showing a dialog askign for the username and password. When you need to connect to the database, you can just use the getUsername and getPassword methods to retrieve the stored values. The login credentials will not be hard-coded into your binaries, so decompilation will not pose a security risk.

Important Note: The preference files are just plain text XML files. Make sure you take appropriate steps to prevent unauthorized users from viewing the raw files (UNIX permissions, Windows permissions, et cetera). In Linux, at least, this isn't a problem, because calling Preferences.userNodeForPackage will create the XML file in the current user's home directory, which is non-readable by other users anyway. In Windows, the situation might be different.

More Important Notes: There has been a lot of discussion in the comments of this answer and others about what the correct architecture is for this situation. The original question doesn't really mention the context in which the application is being used, so I will talk about the two situations I can think of. The first is the case in which the person using the program already knows (and is authorized to know) the database credentials. The second is the case in which you, the developer, are trying to keep the database credentials secret from the person using the program.

First Case: User is authorized to know the database login credentials

In this case, the solution I mentioned above will work. The Java Preference class will stored the username and password in plain text, but the preferences file will only be readable by the authorized user. The user can simply open the preferences XML file and read the login credentials, but that is not a security risk because the user knew the credentials to begin with.

Second Case: Trying to hide login credentials from the user

This is the more complicated case: the user should not know the login credentials but still needs access to the database. In this case, the user running the application has direct access to the database, which means the program needs to know the login credentials ahead of time. The solution I mentioned above is not appropriate for this case. You can store the database login credentials in a preferences file, but he user will be able to read that file, since they will be the owner. In fact, there is really no good way to use this case in a secure way.

Correct Case: Using a multi-tier architecture

The correct way to do it is to have a middle layer, in between your database server and your client application, that authenticates individual users and allows a limited set of operations to be performed. Each user would have their own login credentials, but not for the database server. The credentials would allow access to the middle layer (the business logic tier) and would be different for each user.

Every user would have their own username and password, which could be stored locally in a preferences file without any security risk. This is called a three-tier architecture (the tiers being your database server, business logic server, and client application). It is more complex, but it really is the most secure way to do this sort of thing.

The basic order of operations is:

  1. Client authenticates with business logic tier using the user's personal username/password. The username and password are known to the user and are not related to the database login credentials in any way.
  2. If authentication succeeds, the client makes a request to the business logic tier asking for some information from the database. For example, an inventory of products. Note that the client's request is not a SQL query; it is a remote procedure call such as getInventoryList.
  3. The business logic tier connects to the database and retrieves the requested information. The business logic tier is in charge of forming a secure SQL query based on the user's request. Any parameters to the SQL query should be sanitized to prevent SQL injection attacks.
  4. The business logic tier sends the inventory list back to the client application.
  5. The client displays the inventory list to the user.

Note that in the entire process, the client application never connects directly to the database. The business logic tier receives a request from an authenticated user, processes the client's request for an inventory list, and only then executes a SQL query.

各自安好 2024-07-18 14:59:21

将密码放入应用程序将读取的文件中。 切勿在源文件中嵌入密码。 时期。

Ruby 有一个鲜为人知的模块 DBI::DBRC 用于此类用途。 我毫不怀疑 Java 也有类似的东西。 无论如何,写一篇并不难。

Put the password into a file that the application will read. NEVER embed passwords in a source file. Period.

Ruby has a little-known module called DBI::DBRC for such usage. I have no doubt that Java has an equivalent. Anyway, it is not difficult to write one.

箜明 2024-07-18 14:59:21

您正在编写网络应用程序吗? 如果是这样,请使用 JNDI 在应用程序外部对其进行配置。 此处提供了概述:

JNDI 提供了一种统一的方式
用于查找和访问远程的应用程序
通过网络提供服务。 遥控器
服务可以是任何企业服务,
包括消息服务或
特定于应用程序的服务,但是,
当然,一个 JDBC 应用程序是
主要对数据库感兴趣
服务。 一旦 DataSource 对象被
使用 JNDI 创建并注册
命名服务,应用程序可以使用
用于访问该数据源的 JNDI API
对象,然后可用于
连接到数据源
代表。

Are you writing a web application? If so, use JNDI to configure it externally to the application. An overview is available here:

JNDI provides a uniform way for an
application to find and access remote
services over the network. The remote
service may be any enterprise service,
including a messaging service or an
application-specific service, but, of
course, a JDBC application is
interested mainly in a database
service. Once a DataSource object is
created and registered with a JNDI
naming service, an application can use
the JNDI API to access that DataSource
object, which can then be used to
connect to the data source it
represents.

伤感在游骋 2024-07-18 14:59:21

无论您做什么,敏感信息都会存储在某个文件中。 你的目标是让它尽可能地难以获得。 您能实现多少取决于您的项目、需求和公司的资金实力。

最好的方法是不要在任何地方存储任何密码。 这是通过使用哈希函数生成和存储密码哈希来实现的:

hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366

哈希算法是单向函数。 他们可以转换任意数量的数据
成一个固定长度的“指纹”,无法逆转。 他们也
具有这样的性质:如果输入发生哪怕很小的变化,
生成的哈希值完全不同(参见上面的示例)。 这
非常适合保护密码,因为我们想要存储密码
即使密码文件本身是保护它们的形式
受到损害,但同时,我们需要能够验证
用户密码正确。

不相关的说明:在互联网的旧时代,当您点击“忘记密码”链接时,网站会通过电子邮件向您发送纯文本密码。 他们可能将它们存储在某个数据库中。 当黑客获得对其数据库的访问权限时,他们将获得所有密码。 由于许多用户会在多个网站上使用相同的密码,这是一个巨大的安全问题。 幸运的是,现在这并不常见。

现在问题来了:存储密码的最佳方式是什么? 我认为这个(身份验证和用户管理服务stormpath的)解决方案非常糟糕理想的一种:

  1. 您的用户输入凭据,并根据
    密码
  2. 散列 生成并存储密码散列,而不是密码
  3. 散列执行多次
  4. 使用随机生成的盐生成散列 散列
  5. 用私钥
  6. 加密 私钥存储在与散列不同的物理位置
  7. 私钥基于时间时尚更新
  8. 加密的哈希值被分成块
  9. 这些块存储在物理上独立的位置

显然你不是谷歌或银行,所以这对你来说是一个过度的解决方案。 但随之而来的问题是:您的项目需要多少安全性,您有多少时间和金钱?

对于许多应用程序来说,尽管不推荐,但在代码中存储硬编码密码可能是一个足够好的解决方案。 但是,通过从上面的列表中轻松添加几个额外的安全步骤,您可以使您的应用程序更加安全。

例如,假设步骤 1 不是您的项目可接受的解决方案。 您不希望用户每次都输入密码,或者您甚至不希望/不需要用户知道密码。 但您仍然在某处拥有敏感信息,并且您希望保护这些信息。 您有一个简单的应用程序,没有服务器来存储您的文件,否则这对您的项目来说太麻烦了。 您的应用程序在无法安全存储文件的环境中运行。 这是最糟糕的情况之一,但仍然通过一些额外的安全措施,您可以获得更安全的解决方案。 例如,您可以将敏感信息存储在文件中,并且可以加密该文件。 您可以将加密私钥硬编码在代码中。 您可以混淆代码,从而使其他人更难破解它。 有许多用于此目的的库,请参阅此链接。 (我想再次警告您,这并不是 100% 安全。拥有正确知识和工具的聪明黑客可以破解此问题。但根据您的要求和需求,这对您来说可能是一个足够好的解决方案)。

No matter what you do, the sensitive information will be stored in some file somewhere. Your goal is to make it as difficult to get as possible. How much of this you can achieve depends on your project, needs and thickness of your company's wallet.

The best way is not to store any passwords anywhere. This is achieved by using hash functions to generate and store password hashes:

hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366

Hash algorithms are one way functions. They turn any amount of data
into a fixed-length "fingerprint" that cannot be reversed. They also
have the property that if the input changes by even a tiny bit, the
resulting hash is completely different (see the example above). This
is great for protecting passwords, because we want to store passwords
in a form that protects them even if the password file itself is
compromised, but at the same time, we need to be able to verify that a
user's password is correct.

Unrelated note: In the old days of the internet, when you click 'forgot my password' link, websites would email you your plain text password. They were probably storing those in a database somewhere. When hackers gained access to their database, they would gain access to all the passwords. Since many users would use the same password in multiple websites, this was a huge security problem. Luckily, nowadays this is not the common practice.

Now comes the question: what's the best way to store passwords? I would consider this (authentication and user management service stormpath's) solution a pretty damn ideal one:

  1. Your user enters the credentials, and this is validated against the
    password hash
  2. Password hashes are generated and stored, not passwords
  3. Hashes are performed multiple times
  4. Hashes are generated using a randomly generated salt
  5. Hashes are encrypted with a private key
  6. Private key is stored at a physically different place than hashes
  7. Private keys are on a time-based fashion updated
  8. Encrypted hashes are divided into chunks
  9. These chunks are stored in physically separate locations

Obviously you're not the google or a bank, so this is an overkill solution for you. But then comes the question: How much security your project requires, how much time and money you have?

For many applications, although not recommended, storing hard-coded password in the code might be a good enough solution. However, by easily adding couple of extra steps of security from the above list, you can make your application much more secure.

For example, let's assume step 1 is not be an acceptable solution for your project. You don't want users to enter password every time, or you don't even want/need users to know the password. Still you have sensitive information somewhere and you want to protect this. You have a simple application, there is no server to store your files or this is too much hassle for your project. Your application runs on environments where it is not possible to have files securely stored. This is one of the worst case, but still with some additional security measure you can have much safer solution. For example, you can store the sensitive information in a file, and you can encrypt the file. You can have the encryption private key hard coded in the code. You can obfuscate the code, so you make it a bit more difficult for someone to crack it. There are many libraries exists for this purpose, see this link. (I want to warn you one more time that this is not 100% secure. A smart hacker with right knowledge and tools can hack this. But based on your requirements and needs, this might be a good enough solution for you).

夜无邪 2024-07-18 14:59:21

此问题展示了如何在加密文件中存储密码和其他数据:Java 256-位 AES 基于密码的加密

This question shows how to store passwords and other data in an encrypted file: Java 256-bit AES Password-Based Encryption

虫児飞 2024-07-18 14:59:21

MD5是一种哈希算法,而不是一种加密算法,总之你无法取回你的哈希值,你只能进行比较。
理想情况下,应该在存储用户身份验证信息而不是数据库用户名和密码时使用它。
db 用户名和密码应该加密并保存在配置文件中,以最少的方式进行。

MD5 is a hash algorithm, not an encryption algorithm, in short u cant get back wat u hashed, u can only compare.
It should ideally be used when storing the user authentication information and not db username and password.
db username and pwd should be encrypted and kept in a config file, to do the least.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文