【问题标题】:Where should I keep the credentials of my database?我应该将数据库的凭据保存在哪里?
【发布时间】:2014-07-04 19:39:27
【问题描述】:

将数据库的用户名和密码保存在一个xml文件中并将其导入spring security的安全文件中是一个好主意吗?有没有更好的选择?如果我需要加密密码怎么做以及如何在 phpMyAdmin 上找到密码的加密版本? MySQL

login-service.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

   <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">

    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost/muDB" />
    <property name="username" value="jack" />
    <property name="password" value="alex123432" />
   </bean>

</beans>

myproject-security.xml

      ....
    <beans:import resource='login-service.xml'/> 
      ....

请注意:由于所有与用户相关的密码都已加密,我只打算隐藏 DATABASE 本身的密码,而不是表列。我的应用程序将使用此密码连接到数据库。

【问题讨论】:

  • @user3580294 那么如何加密数据库的密码呢?或者把它们放在哪里?
  • 这是数据库密码而不是表中的那些密码字段。流程一样吗?
  • 即便如此,您也不想明文存储密码或对其进行加密。我相信程序会相同或至少相似。
  • 这是否意味着我应该加密它们并将加密值复制到我的代码和 mysql 的密码部分中!!!
  • @user3580294 无法对需要用于纯文本的密码进行哈希处理(如 connect 到数据库) - 最好发送哈希作为纯文本挑战。

标签: java mysql tomcat spring-security


【解决方案1】:

首先,您应该知道,无论您做什么,如果攻击者获得了对您服务器文件的访问权限,他将能够窃取密码。

如果您使用应用服务器的数据源,那么您只需将明文密码的位置移动到不同的文件中。

如果您使用某种形式的加密来避免存储明文密码,您的应用仍然需要使用它已经拥有的另一个密码对其进行解密。如果攻击者不遗余力地访问您的系统,您可以相当确信他也会知道这一点。您所做的是混淆(并获得虚假的安全感),而不是真正保护它。

更安全的解决方案是让用户在您的应用启动期间提供密码(或解密数据库密码的密码),但这会使管理变得非常困难。而且,如果您已经对某人可以访问您的服务器感到偏执(良好的安全性,而不是疯狂的那种),您应该考虑数据库密码将驻留在系统内存中。

除此之外,请将您的密码保存在您的配置文件中(您可以确信服务器不会向外界显示),锁定您的系统并仅授予数据库用户所需的最低权限。

【讨论】:

  • 非常感谢,但是配置文件是什么意思?能给我举个例子吗
  • 唯一安全的解决方案是让用户在应用启动期间提供密码(或解密数据库密码的密码),但这会使管理变得非常困难。 ==> false,如果你可以访问系统,内存快照对于 java 来说是微不足道的。
  • @bestsss 内存快照是什么?能给我举个例子吗?
  • 它是应用程序内存内容的转储。您可以对其进行分析以找到每个变量的值。见这里:docs.oracle.com/javase/6/docs/technotes/tools/share/jmap.html
  • 很多人认为数据库与应用程序位于同一台服务器上,并且它只保存此应用程序的数据...曾经与购买大型 Oracle 安装的公司合作过吗?考虑到这样一个密码将被签入并分发到 N 个开发人员桌面,因此破解一个开发桌面,您可以获得与数据库对话的凭据,而无需破解应用程序服务器。没关系,一个人可能想在 dev/qa/production 环境中运行应用程序,这些环境应该有不同的密码,也可能有不同的用户。
【解决方案2】:

一种选择是使用Jasypt 与它的 Spring 集成,以便能够将用户名/密码作为属性存储在常规属性文件中,但以加密形式。 Jasypt 将透明地处理解密

【讨论】:

  • 但是我们仍然需要提供密钥来解密,我们将那个密钥存储在哪里?
【解决方案3】:

在配置中设置密码真的很糟糕,而且没有灵丹妙药。 但是,此解决方案符合大多数安全性bla-bla-bla。 最重要的是,它还会混淆 SCM 中的凭据。

PropertyPlaceholderConfigurer:

import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

public class EncryptedPropertyPlacementConfigurer extends PropertyPlaceholderConfigurer
{
    /** algorithm used for encrpytion and decryption */
    private static final String ALGORITHM = "PBEWithMD5AndDES";

    /** 8-byte Salt. */
    private static final byte[] SALT = { ... };

    /** Iteration count. */
    private static final int ITERATION_COUNT = 19;

    /** Stores parameter specification. */
    private static final AlgorithmParameterSpec PARAM_SPEC = new PBEParameterSpec(SALT, ITERATION_COUNT);

    //All properties starting with !! will be decrypted.
    private static final String ENCRYPTIGION_LEADIN = "!!";

    public static class EncrypterException extends RuntimeException
    {
        private static final long serialVersionUID = -7336009350594115318L;

        public EncrypterException(final String message, final Throwable cause)
        {
            super(message, cause);
        }

        public EncrypterException(final String message)
        {
            super(message);
        }
    }

    private static String decrypt(final String passPhrase, final String message)
    {
        // Create the key
        final KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT);
        SecretKey key;
        try
        {
            key = SecretKeyFactory.getInstance(ALGORITHM).generateSecret(keySpec);
        }
        catch (final Exception e)
        {
            throw new EncrypterException("Error setting up encryption details.", e);
        }

        if (!Base64.isBase64(message))
        {
            throw new EncrypterException("Message is not a valid base64 message.");
        }

        final String result;
        try
        {
            final Cipher cipher = Cipher.getInstance(ALGORITHM);

            cipher.init(Cipher.DECRYPT_MODE, key, PARAM_SPEC);

            final byte[] dec = Base64.decodeBase64(message);

            result = new String(cipher.doFinal(dec), "UTF-8");
        }
        catch (final Exception e)
        {
            throw new EncrypterException("Error decrypting content.", e);
        }

        return result;
    }

    @Override
    protected String convertPropertyValue(final String originalValue)
    {
        if (StringUtils.isNotBlank(originalValue) && originalValue.startsWith(ENCRYPTIGION_LEADIN))
        {
            return decrypt("<Your magic password>", originalValue.substring(2));
        }
        return super.convertPropertyValue(originalValue);
    }

}

你的豆子:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">


    <bean id="propertyPlaceholderConfigurer" class="...EncryptedPropertyPlacementConfigurer ">
        <property name="location" value="classpath:/spring.properties" />
        <property name="ignoreResourceNotFound" value="true" />
    </bean>

   <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">

    <property name="driverClassName" value="${jdbc.driver}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.user}" />
    <property name="password" value="${jdbc.password}" />
   </bean>
</beans>

您的财产文件:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/muDB
jdbc.user=!!ar7CWlcL8eI=
jdbc.password=!!ar7CWlcL8eI=

注意: 如果您使用无限制的 JCE 策略,您还可以使用更好的加密算法,但由于我们只做混淆处理,这将起到作用,并且不会让您以调试会话结束。

更新:

您可以使用它来生成您的密码:

import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import org.apache.commons.codec.binary.Base64;

public class Main
{

    private static class DesEncrypter
    {
        /** algorithm used for encrpytion and decryption */
        private static final String ALGORITHM = "PBEWithMD5AndDES";

        /** 8-byte Salt. */
        private static final byte[] SALT = { <You salt> };

        /** Iteration count. */
        private static final int ITERATION_COUNT = 19;

        /** Stores parameter specification. */
        private static final AlgorithmParameterSpec PARAM_SPEC = new PBEParameterSpec(
            SALT, ITERATION_COUNT);

        /** Key specification. */
        private final KeySpec keySpec;

        /** Secret key. */
        private final SecretKey key;

        public DesEncrypter(final String passPhrase)
        {
            // Create the key
            keySpec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT);
            try
            {
                key = SecretKeyFactory.getInstance(ALGORITHM).generateSecret(keySpec);
            }
            catch (final Exception ex)
            {
                throw new RuntimeException("Could not create DesEncrypter: " + ex.getMessage(), ex);
            }
        }

        public final String encrypt(final String message)
        {
            try
            {
                // Create cipher instance
                final Cipher cipher = Cipher.getInstance(ALGORITHM);
                // Initialize cipher
                cipher.init(Cipher.ENCRYPT_MODE, key, PARAM_SPEC);
                // Encode string
                final byte[] enc = cipher.doFinal(message.getBytes("UTF8"));
                // Encode bytes to base64 to get a string
                return Base64.encodeBase64String(enc);
            }
            catch (final Exception ex)
            {
                throw new RuntimeException("Error encrypting message.", ex);
            }
        }
    }

    public static void main(final String[] args)
    {
        if (args.length == 2)
        {
            System.out.println("!!" + new DesEncrypter(args[0]).encrypt(args[1]));
        }
    }
}

【讨论】:

    【解决方案4】:

    你可以把它放在应用服务器上,通过 jndi 名称获取。

    例如,如果您使用任何 jpa 实现,例如 hibernate/eclipse-link,您可以将其定义如下

    spring-security.xml

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="dataBase.db"/>
    </bean>
    

    persistence.xml

    <persistence-unit name="dataBase.db" transaction-type="JTA">
    ...
        <jta-data-source>java:jboss/datasources/PostgresqlDS</jta-data-source>
    ...
    </persistence-unit>
    

    在应用服务器上,您必须在服务器配置文件中定义与数据库(数据源)的连接。在 Jboss 7 的情况下,它是 stadalone.xml jboss datasource

    【讨论】:

    • 请给我一个例子
    • 很好,但是在哪里设置应用程序使用的密码以及在哪里放置 db 的加密密码?
    • 凭证在应用服务器的数据源中。每个 j2ee 应用服务器都有自己的配置方式,命名为数据源。
    • 我明白了,我去看看我的应用服务器,也就是tomcat。
    • 为了应用程序服务器的完整性,例如Tomcat,您在服务器xml文件中定义数据源,即:“"
    【解决方案5】:

    好旧的鸡和蛋的问题。

    把数据库的用户名和密码保存在一个 xml文件并导入spring security的security文件中?

    这比将其直接存储在源代码中更好,但比让企业应用服务器为您处理(如 SAP NetWeaver 或 Oracle WebLogic)更糟糕。

    好处是您将应用程序与凭据分开,允许特定环境的配置和操作系统安全限制。

    与大多数软件解决方案一样,这取决于。在您的情况下,这取决于应该为此目的付出多少“努力”。

    还有更好的选择吗?

    即使您将凭据存储在文件中,您也应该至少对其进行编码,或者尽可能对其进行加密。但同样,这只会“混淆”真实密码。

    例如,为了使用同步算法进行加密,您将需要一个密钥。那么这个密钥将存储在哪里呢?这是循环安全性,它使破解密码的努力更大,但并没有消除风险。

    建议 1: 使存储凭据的文件只有操作系统管理员用户和您的系统用户才能访问,以便它可以读取它。在它上面使用密钥加密。就我个人而言,我总是使用AES 256 算法。

    建议 2: 不要将其存储在文件中,而是要求基础架构团队(超级操作系统管理员)将加密密码作为系统参数发送给您。将凭证安全的责任委托给基础设施团队。这是AWS Beanstalk integration with RDS 的当前方法。

    如果你对安全着迷:

    • 如果您不信任您的基础架构团队,您可能希望拥有 由人工手动输入的应用程序密码 应用程序启动。您还需要处理它的缺点,例如始终需要有人来启动应用程序和水平缩放。

    • 您可能希望像在 DVD 介质,必须由可操作的服务器插入 成员。您还必须处理设备上的访问 在您的操作系统中。

    不要害怕与您的利益相关者谈论它。问他/他们什么是“足够”可以接受的,并对此感到高兴。

    存储凭据时总会有风险。

    如果我需要加密密码怎么做以及如何找到 phpMyAdmin 上的密码加密版本? MySQL

    避免到处复制您的密码。您应该在服务器内部处理凭据。

    对于一个解决方案,我们制作了一个只能由管理员通过 X11 协议或控制台访问的自定义软件,仅基于 Java Crypt API。该软件旨在以安全的方式更改凭据。

    密码始终在安全的 SSH 连接(如果是远程)甚至本地访问中传输,并且仅在管理员和服务器之间传输,因为权限是在操作系统中以这种方式定义的。

    对于 PhpMyAdmin,它有自己的密码处理方式,如果不进行广泛的定制工作,您很可能无法将这两种解决方案集成到一个中。不要为 PhpMyAdmin 或任何其他 MySQL 客户端存储密码,这只会增加您的安全风险。

    【讨论】:

      【解决方案6】:

      你可以保存在属性文件中

      在我的项目中,我在 STS IDE 的 META-INF 下创建了一个 database.properties

      【讨论】:

      • 您能否详细说明如何检索context.xml 中的属性值? (不使用Spring
      【解决方案7】:

      在 Rails 中,我将环境变量中的敏感数据保存在 .env 文件中,并将该文件添加到 .gitignore。我不确定你是否可以做类似的事情。

      "If I need to encrypt the password how to do it and how to find the encrypted version of password on phpMyAdmin"

      您可以通过以下方式创建加密密码:

      http://bcrypthashgenerator.apphb.com/

      ..然后你会知道密码是什么,你可以通过phpMyadmin将加密版本添加到正确的表中。

      您可以将密码保存在本地存储库中,而不将它们部署到远程吗?我想知道您是否可以设置与 Rails ENV 类似的场景?

      你有没有看过这样的东西:http://www.jasypt.org/spring3.html

      【讨论】:

        【解决方案8】:

        正如其他人所提到的,如果您将密码存储在服务器上,那么如果攻击者可以访问您的计算机,您将无能为力。唯一可行的替代方法是使用 SSL 连接和基于证书的身份验证。

        上述方法在SO上有already been discussedanswer has been provided

        【讨论】:

          【解决方案9】:

          建议在您的应用服务器中使用数据源配置。并尝试使用使用上下文的 JNDI 查找来使用它

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-12-28
            • 1970-01-01
            • 1970-01-01
            • 2021-09-08
            • 2014-11-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多