我应该在哪里保留数据库的凭据?

时间:2014-05-16 06:20:24

标签: java mysql tomcat spring-security

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

登录-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本身的密码,而不是表格列。我的应用程序将使用此密码连接到数据库。

9 个答案:

答案 0 :(得分:29)

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

如果您使用应用服务器的数据源,则只需将明文密码的位置移动到其他文件即可。

如果您使用某种形式的加密来避免存储明文密码,那么您的应用仍然需要使用其已有的其他密码对其进行解密。如果攻击者竭尽全力访问您的系统,您可以相信他也会知道这一点。你正在做的是混淆(并获得虚假的安全感),而不是实际保护它。

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

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

答案 1 :(得分:6)

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

答案 2 :(得分:5)

配置中的密码真的很糟糕,没有银弹。 然而,这种解决方案适用于大多数安全性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]));
        }
    }
}

答案 3 :(得分:4)

您可以将其保存在应用程序服务器上并通过jndi名称获取。

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

弹簧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

答案 4 :(得分:3)

古老的鸡肉和鸡蛋问题。

  

将数据库的用户名和密码保存在中是个好主意    xml文件并将其导入spring security的安全文件中?

这比在源代码中明确存储它更好,但比让企业应用程序服务器处理ya更糟糕(例如SAP NetWeaver或Oracle WebLogic)。

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

与大多数软件解决方案一样,它取决于。在你的情况下,它取决于应该为此目的投入多少“努力”。

  

还有更好的选择吗?

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

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

建议1:使存储凭据的文件只能供OS管理员用户和系统用户访问,以便它可以读取。在其上使用密钥加密。我个人总是使用AES 256算法。

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

如果你对安全感到疯狂:

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

  • 您可能希望将密码“物理”地处理为一个 必须通过操作插入服务器的DVD媒体 会员。您还必须处理设备上的访问权限 在您的操作系统中。

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

存储凭证时总是存在风险。

  

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

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

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

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

至于PhpMyAdmin,它有自己的处理密码的方式,而且很可能无法在没有大量定制工作的情况下将两种解决方案集成到一起。不要存储PhpMyAdmin或任何其他MySQL客户端的密码,只会增加您的安全风险。

答案 5 :(得分:2)

您可以保留属性文件

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

答案 6 :(得分:1)

在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

答案 7 :(得分:0)

正如其他人提到的,如果您在服务器上存储密码,如果攻击者获得对您计算机的访问权限,则无法执行任何操作。唯一可行的替代方案是使用SSL连接和基于证书的身份验证。

上述方法在SO和already been discussed上有answer has been provided

答案 8 :(得分:0)

建议您在App Server中使用数据源配置。并尝试使用上下文

使用JNDI查找