By and large, the applications that we deploy are configured at runtime by using the Spring PropertyPlaceholderConfigurer. For those of you not familiar with this concept, basically, any environment specific properties in our application or even properties that we just want to be able to easily change, are defined using Ant style property syntax within our Spring beans configuration.

For instance, our DataSource configuration looks something like this:

<bean id="dataSource" class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean"
          destroy-method="close">
      <property name="uniqueResourceName" value="My Primary Datasource"/>
      <property name="driverClassName" value="com.inet.tds.TdsDriver"/>
      <property name="url" value="jdbc:inetdae7a:${database.host}:${database.port}?database=${database.name}&amp;secureLevel=0"/>
      <property name="user" value="${database.user}"/>
      <property name="password" value="${database.password}"/>
      <property name="testQuery" value="select 1"/>
      <property name="maxPoolSize" value="50"/>
      <property name="minPoolSize" value="20"/>
      <!-- Setting reapTimeout=0 because atomikos incorrectly reaps nonxa connections. -->
      <property name="reapTimeout" value="0"></property>
    </bean>

By defining a PropertyPlaceholderConfigurer, we can have Spring inject the appropriate properties for this DataSource when the application starts up:

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <value>classpath:runtime-properties/${env}/my-application.properties</value>
        </property>
        <property name="nullValue">
            <value>null</value>
        </property>
        <!-- Force system properties to override any deployed runtime properties -->
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
    </bean>

For instance, my file runtime-properties/dev/my-application.properties would contain:

database.host=localhost
database.user=myuser
database.password=mypassword
database.name=myapplicationdb
database.port=1433

When we deploy our application, we deploy ALL of the runtime property files for all of the different runtime environments. By injecting the env property into the container at runtime, we tell the application which runtime properties to use, e.g.:

-Denv=dev

This is usually done either in the startup script or the startup configuration for the container. For example, in JBoss, we would add this to the run.conf file or the run.sh script.

This approach opens up some security issues, however. Now, all of my database credentials for all of my runtime environments are packaged up in cleartext in my single WAR artifact. If, for instance, the sys admin who manages our deployments in our testing environments (integration and staging) is not the same sys admin that manages our deployments in our production environment, especially if this is security related (only trusted senior administrators can manage production applications), we have a security hole because the credentials for all environments are stored in the WAR.

Historically, there has been no easy way around this. Typically, one would generate a secret key that could be used to decrypt the passwords if they were encrypted in the application configuration but then if you put this secret key IN the application deployment, you STILL have a security hole–any sufficiently determined adversary can use this key to decrypt your encrypted passwords.

Recently, I stumbled across the Jasypt project. It aims to provide some simplified interfaces into the Java Encryption API’s. The feature it offered that most appealed to me, however, was the integration that it offers for Spring that dovetails nicely with my configuration.

Jasypt offers a special implementation of the PropertyPlaceholderConfigurer called the EncryptablePropertyPlaceholderConfigurer that allows you to encrypt some of your properties in your configuration files and have them decrypted on the fly when the application is starting. What’s more important, is it allows you to pass the encryption key, or ‘password’ into the application as a system property at runtime, decoupling the knowledge of the key from the deployable artifact.

Let’s see how this would work

First we would need to decide on an encryption algorithm and a password. Then we would use the commandline tools that ship with Jasypt to encrypt our database password above:
./encrypt.sh input="mypassword" algorithm=PBEWithMD5AndTripleDES password=jasypt_is_cool
Jasypt will output something that looks like:

----ENVIRONMENT-----------------

Runtime: Sun Microsystems Inc. Java HotSpot(TM) Server VM 10.0-b23

—-ARGUMENTS——————-

algorithm: PBEWithMD5AndTripleDES
input: mypassword
password: jasypt_is_cool

—-OUTPUT———————-

AUP/WSfdvbAfVuBJW/kbXsqh2qu8yWiJ

Then, we update our configuration file and replace:

database.password=mypassword

with:

database.password=ENC(AUP/WSfdvbAfVuBJW/kbXsqh2qu8yWiJ)

The syntax ENC(...) tells the EncryptablePropertyPlaceholderConfigurer that this property is encrypted.

Now, we will add the beans for the encryptor and it’s configuration:

    <bean id="jasyptConfiguration"
          class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig"
          p:algorithm="PBEWithMD5AndTripleDES"
          p:passwordSysPropertyName="jasypt.encryption.password"/>

    <bean id="propertyPasswordEncryptor"
          class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor"
          p:config-ref="jasyptConfiguration"/>

The important thing to take away from the above is the configuration. The property passwordSysPropertyName tells Jasypt that it should load the encryption password from a system property named jasypt.encryption.password.

Now, as I described above, I add a runtime property to my container so that this password gets injected into the runtime environment as a system property:

-Djasypt.encryption.password=jasypt_is_cool

The final step is to change the PropertyPlaceholderConfigurer and wire it up to this encryptor:

    <bean class="org.jasypt.spring.properties.EncryptablePropertyPlaceholderConfigurer">
        <constructor-arg>
          <ref bean="propertyPasswordEncryptor"/>
        </constructor-arg>
        <property name="locations">
          <value>classpath:runtime-properties/${env}/my-application.properties</value>
        </property>
        <property name="nullValue">
          <value>null</value>
        </property>
        <!-- Force system properties to override any deployed runtime properties -->
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
    </bean>

And that’s it! I’ve now added another layer of security to avoid having my production passwords floating around in my deployable artifact. It is generally a good idea to record this configuration in a secure location outside of source control that has limited access. This configuration should include the actual database password, the Jasypt encryption password used and the Jasypt algorithm that was used to generate the encrypted password, at a minimum.

Cheers!

Advertisements