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

Posted

技术标签:

【中文标题】如何保护 MySQL 用户名和密码不被反编译?【英文标题】:How can I protect MySQL username and password from decompiling? 【发布时间】:2010-10-01 08:50:04 【问题描述】:

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

【问题讨论】:

我希望你不介意我重新标记了你的问题。我删除了“用户名”和“密码”,并添加了“逆向工程”和“反编译”。我认为它们比原件更具描述性。顺便说一句,关于基本面的好问题! 请注意,您使用 Java 的事实在这里并不重要。以几乎相同的方式使用任何语言的硬编码密码都是有问题的(“strings thebinary”也显示了 C 程序中的字符串常量)。 @saua:没错,但也许有人会发布一些示例代码,介绍如何在 Java 中解耦用户名和密码。如果我有时间,我什至可以自己做。 我注意到很多答案认为您试图在运行应用程序的用户正常的情况下向未经授权的用户隐藏用户名/密码。我相信您想对 每个人 隐藏密码。请在问题中澄清这一点。 例如,假设凭据用于连接到除应用程序以外的任何人都无法登录的服务器。 【参考方案1】:

切勿将密码硬编码到您的代码中。这是最近在Top 25 Most Dangerous Programming Mistakes:

硬编码一个秘密帐户和 进入您的软件的密码是 非常方便——对于熟练的 逆向工程师。如果密码是 你所有的软件都一样, 那么每个客户都会变得脆弱 当那个密码不可避免地变成 已知。而且因为它是硬编码的, 修复起来非常痛苦。

您应该将配置信息(包括密码)存储在应用程序启动时读取的单独文件中。这是防止密码因反编译而泄漏的唯一真正方法(从一开始就不要将其编译成二进制文件)。

有关此常见错误的更多信息,您可以阅读CWE-259 article。这篇文章包含更全面的定义、示例以及有关该问题的许多其他信息。

在 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 中,情况可能会有所不同。

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

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

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

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

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

正确案例:使用多层架构

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

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

基本的操作顺序是:

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

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

【讨论】:

这究竟是如何阻止某人获取用户名/密码的?那你不能从文件中读取它吗? 正如我在回答中所说,如果您的文件权限设置正确,则只有运行该程序的用户才能读取该首选项文件。在 UNIX 环境中,这是自动完成的。 Windows 可能需要额外的步骤(我真的不确定,因为我不经常使用 Windows)。 我认为这个想法是运行应用程序的用户不是你想要阻止它的用户。如果是这种情况,那么您必须对其进行加密。 是的,迈克尔是正确的。本质上,这个想法是您已经知道用户名/密码,因此无需对自己隐藏它。但是,它将通过文件权限对其他用户隐藏。 如果您正在为用户部署(示例)数据库编辑应用程序并且您不希望他们知道数据库用户名和密码,那么您的解决方案架构错误,客户端软件应该与执行 DB 工作的服务器(例如通过 Web 服务)进行通信。【参考方案2】:

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

Ruby 有一个鲜为人知的模块DBI::DBRC 用于此类用途。我毫不怀疑Java有一个等价物。反正写一个也不难。

【讨论】:

虽然这让以后更改密码稍微容易一些,但并不能解决基本的安全问题。 是的。另请参阅 William Brendel 的回答。 Keltia 和我指出的方法是处理这个问题的公认方法。将您的登录凭据与编译的代码分离是最基本的软件安全实践之一。将登录凭据放在单独的文件中是实现此目的的有效方法。 此外,您的配置信息在纯文本文件中的事实应该被操作系统限制取消。例如,在 UNIX 中,明文文件应该归运行程序的用户所有,权限为 0600,因此只有所有者才能读取。 OK,所以文件只能由运行程序的用户读取。伟大的。那解决不了任何问题。 :-) 我,我们试图对其保密的用户,可以像应用程序一样轻松地阅读它......【参考方案3】:

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

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

【讨论】:

提供的链接是错误的【参考方案4】:

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

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

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

哈希算法是单向函数。他们将任何数量的数据 变成不可逆转的定长“指纹”。他们也 有这样的性质,如果输入改变一点点, 结果哈希是完全不同的(见上面的例子)。这 非常适合保护密码,因为我们要存储密码 即使密码文件本身是保护它们的形式 妥协,但同时,我们需要能够验证 用户密码正确。

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

现在问题来了:存储密码的最佳方式是什么?我认为this (authentication and user management service stormpath's) 解决方案非常理想:

    您的用户输入凭据,并根据 密码哈希 生成和存储密码哈希,而不是密码 多次执行哈希 哈希是使用随机生成的盐生成的 哈希使用私钥加密 私钥存储在物理上不同于散列的位置 私钥按时间更新 加密的哈希被分成块 这些块存储在物理上不同的位置

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

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

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

【讨论】:

【参考方案5】:

这个问题展示了如何在加密文件中存储密码和其他数据:Java 256-bit AES Password-Based Encryption

【讨论】:

源代码中的某个地方仍然需要解密加密密码来创建连接,这个被认为的密码仍然存在。【参考方案6】:

MD5是散列算法,不是加密算法,总之你不能取回你的散列,你只能比较。 理想情况下,它应该在存储用户身份验证信息而不是数据库用户名和密码时使用。 db username 和 pwd 应该被加密并保存在一个配置文件中,至少这样做。

【讨论】:

我听说有人会生成所有可能的字符串组合并存储其对应的 MD5 哈希值。因此,当他们找到某人的 MD5 哈希时,他们只需找到他们存储的哈希,并得到相应的字符串。

以上是关于如何保护 MySQL 用户名和密码不被反编译?的主要内容,如果未能解决你的问题,请参考以下文章

如何阻止java反编译

如何有效的防止Java程序被反编译和破解

一些防止 Java 代码被反编译的方法

如何防止SWF文件被反编译

小妙招:如何防止你的 jar 包被反编译?

面试被问 | 防止 Java 代码被反编译的方法有几种?