Jack
Jack

Reputation: 6600

Where should I keep the credentials of my database?

Is it a good idea to keep the username and password of database in a xml file and import it into security file of the spring security ? is there any better option? If I need to encrypt the password how to do it and how to find the encrypted version of password on 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'/> 
      ....

PLEASE NOTE: As all user related passwords are already encrypted, I am only intended to hide the password of the DATABASE itself not table columns. This password would be used by my application to get connected to the database.

Upvotes: 36

Views: 56995

Answers (9)

Evandro Pomatti
Evandro Pomatti

Reputation: 15094

Good and old chicken and egg problem.

Is it a good idea to keep the username and password of database in a xml file and import it into security file of the spring security ?

It is a better idea than storing it plain in the source code, but worse than having an enterprise application server handling that for ya (like SAP NetWeaver or Oracle WebLogic).

The good part is that you separate your application from the credentials, allowing for environment specific configuration and OS security restrictions.

Like most of software solutions, it depends. And in your case, it depends on how much "effort" is supposed to be dedicated for that purpose.

Is there any better option?

Even if you are storing the credentials in a file you should at the minimum encode it or if possible encrypt it. But again, this will only "obfuscate" the real password.

For instance, in order to encrypt with a synchronous algorithm you will need a secret key. So where will this secret key be stored? This is circular security which makes the effort to hack the password greater but does not eliminate the risk.

Suggestion 1: Make the file that store the credentials only accessible for the OS admin user and your system user as well so it can read it. Use secret key encryption on top of it. Personally I always go with the AES 256 algorithm.

Suggestion 2: Instead of storing it in a file, ask for the infrastructure team (super OS admins) to send you the encrypted password as a system parameter. Delegate the resposability of the credentials security to the infrastructure team. This is the current approach for AWS Beanstalk integration with RDS.

If you are crazy about security:

  • If you do not trust your infrastructure team you might wanna have the password of the application to be manually entered by a human on the application start-up. You will need to handle the cons of it as well, like always needing a human presence for the start of the application, and horizontal scaling.

  • You might wanna have the password "physically" handled like within a DVD media which has to be inserted on the server by a operational member. As well you will have to handle the access on the device within your OS.

Do not be affraid of talking to your stakeholders about it too. Ask him/them what is the "enough" acceptable and be happy about it.

There will always be a risk when storing credentials.

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

Avoid copying your password around. You should handle the credentials inside your server.

For one solution we made a custom software only accessible by Admins via X11 protocol or console, based only in the Java Crypt API. This software was designed for changing the credentials in a secure way.

The password always transit in secure SSH connections (if remote) or even local accessed, and only between Admins and Server since the permissions were defined that way in the OS.

As for the PhpMyAdmin it has its own way for handling passwords, and you most likely wont be able to integrate both solutions into one without a wide customization effort. Do not store passwords for PhpMyAdmin or any other MySQL client, it will only increase your security risk.

Upvotes: 4

Jordan
Jordan

Reputation: 1670

In Rails, I keep the sensitive data in Environment variables in a .env file and add the file to .gitignore. I am not sure if you can do something similar.

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

You could create an encrypted password through something like this:

http://bcrypthashgenerator.apphb.com/

..and then you would know what the password was and you could add the encrypted version to the correct table through phpMyadmin.

Could you just keep the passwords in your local repo, but not deploy them to the remote? I am wondering if you could setup a similar scenario to Rails ENV?

Have you looked at something like this: http://www.jasypt.org/spring3.html

Upvotes: 1

saysiva
saysiva

Reputation: 930

Recommended to use Datasource configuration in your App Server. And try to use that using JNDI lookup using context

Upvotes: 0

Hannes
Hannes

Reputation: 2073

Having passwords in configuration realy sucks and there is no silver bullet for it. However this solution is complient with most security bla-bla-bla. On top it will also obfuscate the credentials in your 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);
    }

}

Your Bean:

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

Your Property-File:

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

Note: If you use the unlimited JCE Policy, you can also use better encryption algorithm but since we do nothing more than obfuscation, this will do the trick and won't let you end up with debugging sessions.

Update:

You can use this to generate your password:

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]));
        }
    }
}

Upvotes: 7

jeconom
jeconom

Reputation: 784

First of all, you should be aware that no matter what you do, if an attacker gains access to your server files, he will be able to steal the password.

If you use an app server's datasource then you just move the location of the plaintext password to a different file.

If you use some form of encryption to avoid storing a plaintext password your app will still have to decrypt it with another password which it will already have. If an attacker goes to great lengths to gain access to your system you can be fairly confident that he will know that too. What you are doing is obfuscating (and gaining a false sense of security) rather than actually securing it.

A more secure solution is for a user to provide the password (or a password to decrypt the DB password) during your app's startup, but that will make administration really difficult. And if you are already paranoid (the good security kind, not the crazy kind) that someone has access to your server, you should consider that the DB password will reside in the system memory.

Other than that, keep your password in your configuration file (which you can be fairly confident that the server won't show to the outside world), lock down your system and give the database user only the minimum permissions required.

Upvotes: 49

mindas
mindas

Reputation: 26703

As someone else mentioned, if you are storing passwords on the server, there is nothing you can do if attacker gains access to your machine. The only viable alternative is to use SSL connection and certificate-based authentication.

The above method has already been discussed on SO and answer has been provided.

Upvotes: 0

bmlynarczyk
bmlynarczyk

Reputation: 798

You can hold it on application server and get by jndi name.

For example if you use any jpa implementation like hibernate/eclipse-link you can define it as follow

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>

On application server you have to define conection to database(datasource) in server config file. In Jboss 7 case it is stadalone.xml jboss datasource.

Upvotes: 4

geoand
geoand

Reputation: 63991

One option is to use Jasypt with it's Spring integration in order to be able to store the usename/password as properties in a regular properties file but in an encrypted form. Jasypt will transparently take care of the decryption

Upvotes: 7

SpringLearner
SpringLearner

Reputation: 13844

you can keep in properties file

In my project I have created a database.properties under META-INF in STS IDE

Upvotes: 1

Related Questions