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


87

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


希望您不要介意我重新提出您的问题。我删除了“用户名”和“密码”,并添加了“反向工程”和“反编译”。我认为它们比原文更具描述性。顺便问一下,关于基本面的很好的问题!
威廉·布伦德尔

6
请注意,您使用Java的事实在这里并不是无关紧要的。用几乎任何一种方式用任何语言进行硬编码的密码都是有问题的(“ strings thebinary”也显示C程序中的字符串常量)。
约阿希姆·绍尔

@saua:是的,但是也许有人会发布一些示例代码,说明如何在Java中解耦用户名和密码。如果有时间,我什至可以自己做。
威廉·布伦德尔

1
我注意到很多答案都认为您试图在运行该应用程序的用户正常的情况下向未经授权的用户隐藏用户名/密码。我相信您想对所有人隐藏密码。请在问题中对此进行澄清。
pek

1
例如,假设凭据用于连接到服务器,该应用程序以外的任何人都无法登录。
pek

Answers:


123

切勿将密码硬编码到您的代码中。最近在最危险的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中,情况可能有所不同。

更重要的注意事项:在此答案的注释以及其他内容的讨论中,有很多讨论都针对这种情况使用了正确的体系结构。最初的问题并没有真正提到应用程序使用的上下文,因此我将讨论我能想到的两种情况。第一种情况是使用该程序的人已经知道(并被授权知道)数据库凭据。第二种情况是您(开发人员)试图对使用该程序的人员保密数据库凭据。

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

在这种情况下,我上面提到的解决方案将起作用。JavaPreference类将以纯文本形式存储用户名和密码,但是首选项文件仅可由授权用户读取。用户可以简单地打开首选项XML文件并读取登录凭证,但这不会带来安全风险,因为用户知道其开头就是凭证。

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

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

正确的情况:使用多层体系结构

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

每个用户都有自己的用户名和密码,可以将它们存储在本地的首选项文件中,而不会带来任何安全风险。这称为三层体系结构(这些层是数据库服务器,业务逻辑服务器和客户端应用程序)。它更复杂,但这实际上是执行这种操作的最安全的方法。

操作的基本顺序是:

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

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


4
这究竟如何阻止某人获取用户名/密码?那你不能从文件中读取吗?
乔·菲利普斯

正如我在回答中所说,如果您的文件许可权设置正确,则只有运行该程序的用户才能对该首选项文件进行读取访问。在UNIX环境中,这是自动完成的。Windows可能需要其他步骤(我不确定,因为我使用Windows的次数很少)。
威廉·布伦德尔

我认为这种想法是,运行该应用程序的用户不是您要阻止其运行的用户。如果真是这样,那么您必须对其进行加密。
Michael Haren

是的,迈克尔是正确的。从本质上讲,您的想法是您已经知道用户名/密码,因此无需对您自己隐藏。但是,通过文件许可权,它将对其他用户隐藏。
威廉·布伦德尔

7
如果您正在向用户部署(示例)数据库编辑应用程序,并且您不希望他们知道数据库的用户名和密码,则说明该解决方案的结构错误,并且客户端软件应与服务器通信(通过例如Web服务)来完成数据库工作。
JeeBee

15

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

Ruby有一个鲜为人知的模块,称为DBI :: DBRC。我毫不怀疑Java具有等效功能。无论如何,写一个并不难。


7
虽然这样可以使以后更改密码稍微容易一些,但是并不能解决基本的安全性问题。
Brian Knoblauch

是的,它确实。另请参见William Brendel的答案。
Keltia

1
我和Keltia指出的方法是解决此问题的公认方法。将登录凭据与已编译的代码分离,是最基本的软件安全措施之一。将登录凭据放在单独的文件中是实现此目的的有效方法。
威廉·布伦德尔

此外,您的配置信息位于纯文本文件中的事实应通过操作系统限制来取消。例如,在UNIX中,纯文本文件应由运行该程序的用户拥有,并具有权限0600,因此只有所有者才能读取它。
威廉·布伦德尔

4
好的,因此该文件只能由运行该程序的用户读取。大。那解决不了什么。:-)我(我们试图防止密码泄露的用户)可以像应用程序一样轻松地读取它……
Brian Knoblauch 2009年

3

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

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


提供的链接不好
James Oravec

2

无论您做什么,敏感信息都将存储在某个文件中的某个位置。您的目标是使获取尽可能困难。可以实现多少取决于您的项目,需求和公司钱包的厚度。

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

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

哈希算法是一种方法功能。他们将任何数量的数据转换为不可逆的固定长度的“指纹”。它们还具有以下特性:如果输入变化很小,则生成的哈希值将完全不同(请参见上面的示例)。这对于保护密码非常有用,因为即使密码文件本身受到破坏,我们也希望以一种保护密码的形式存储密码,但是与此同时,我们需要能够验证用户密码是否正确。

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

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

  1. 您的用户输入了凭据,并且已针对密码哈希进行了验证
  2. 生成并存储密码哈希,而不是密码
  3. 哈希执行多次
  4. 使用随机生成的盐生成哈希
  5. 哈希使用私钥加密
  6. 私钥存储在与散列不同的物理位置
  7. 私钥以基于时间的方式更新
  8. 加密的哈希分为多个块
  9. 这些块存储在物理上分开的位置

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

对于许多应用程序,尽管不建议这样做,但在代码中存储硬编码密码可能是一个很好的解决方案。但是,通过轻松地从上述列表中添加几个额外的安全步骤,可以使您的应用程序更加安全。

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



0

MD5是一种哈希算法,而不是加密算法,总之,您无法通过哈希算法找回密码,只能进行比较。理想情况下,应在存储用户身份验证信息而不是db用户名和密码时使用。db用户名和pwd应该被加密并保存在配置文件中,以尽量减少。


我听说有人会生成所有可能的字符串组合并存储其相应的MD5哈希值。因此,当他们找到某人的MD5哈希时,他们只是找到他们存储的哈希,就得到了相应的字符串。
2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.