Wednesday, 22 March 2017

Tomcat Datasource Username Password Encryption

You may refer to (https://tomcat.apache.org/tomcat-8.0-doc/jndi-datasource-examples-howto.html) for details of how to configure a JDBC data source and use it in a web application, this post is going to talk about how to secure database user name and password in the datasource configuration.




STEP 1 : Download Jasypt
We are going to use jasypt for sensitive data encryption and decryption , you may download command line tool from http://www.jasypt.org.






STEP 2 : Encrypt Your Credentials
After downloading jasypt, extract the zip file anywhere and go into bin folder run below commands : 
$ ./encrypt.sh input="your-db-user" password=secret-key
$ ./encrypt.sh input="your-db-password" password=secret-key
$ ./encrypt.sh input="your-db-url" password=secret-key

"secret-key" would be needed for decryption, so you need to keep it safe and in this post we are storing and retrieving the secret key from system enviroment variable.






STEP 3 : Configure Your Datasource
Use encrypted values in step 2 to configure your secured datasource in context.xml as below :

<?xml version='1.0' encoding='utf-8'?>
<Context>
<Resource name="jdbc/yourDB"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
factory="org.junjun.util.secured.resources.datasource.EncryptedDataSourceFactory"
url="e6nNC2NVXThPKSyKMvCiZTa4beL/BHxyu+188EeeNWq10ynepC2f4fXHzlKKdo9288uNOStZwa6Ztu0h7TrWVAJIrurKQkfO"
username="PzyHPd4xkXJYvoN6ztoLWg=="
password="zeBxd99nNw2Hg6f4g/Rt8PkB7+uHOwVD"
maxActive="20"
maxIdle="10"
maxWait="-1" />
<ResourceLink name="jdbc/yourDB" global="jdbc/yourDB" type="javax.sql.DataSource" />
</Context>





STEP 4 : Create Your Own Datasource Factory
As you can see from step 3 we add an attribute "factory" to "Resource" tag which is going to be used to initialise the datasource in the same time decrypt database connection credentials.
package org.junjun.util.secured.resources.datasource;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
public class EncryptedDataSourceFactory extends BasicDataSourceFactory {
private String USERNAME_TAG = "username";
private String PASSWORD_TAG = "password";
private String URL_TAG = "url";
private StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
private String password = System.getenv("secret-key");
public EncryptedDataSourceFactory() {
encryptor.setPassword(this.password);
}
@Override
public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable env) throws Exception {
if (obj instanceof Reference) {
setUsername((Reference) obj);
setPassword((Reference) obj);
setURL((Reference) obj);
}
return super.getObjectInstance(obj, name, ctx, env);
}
private void setUsername(Reference ref) {
findDecryptAndReplace(USERNAME_TAG, ref);
}
private void setPassword(Reference ref) {
findDecryptAndReplace(PASSWORD_TAG, ref);
}
private void setURL(Reference ref) {
findDecryptAndReplace(URL_TAG, ref);
}
private void findDecryptAndReplace(String refType, Reference ref) {
Integer idx = find(refType, ref);
if (idx == null) {
System.out.println("The \"" + refType + "\" name/value pair was not found"
+ " in the Reference object. The reference Object is" + " " + ref.toString());
} else {
String decrypted = decrypt(ref.get(refType).getContent().toString());
replace(idx, refType, decrypted, ref);
}
}
private void replace(int idx, String refType, String newValue, Reference ref) {
ref.remove(idx);
ref.add(idx, new StringRefAddr(refType, newValue));
}
private String decrypt(String input) {
return encryptor.decrypt(input);
}
private Integer find(String addrType, Reference ref) {
Integer index = null;
Enumeration<RefAddr> enu = ref.getAll();
for (int i = 0; enu.hasMoreElements(); i++) {
RefAddr addr = (RefAddr) enu.nextElement();
if (addr.getType().compareTo(addrType) == 0)
index = i;
}
return index;
}
}






STEP 5 : Build Your Own Datasource Factory into a Jar File
Please refer to https://github.com/junjun-dachi/java-util/tree/master/secured-resources-util for the maven project which contains source code of EncryptedDataSourceFactory.


Build "secured-resources-util" project with maven , system would generate a jar file named "secured-resources-1.0.jar".



STEP 6 : Copy Jar Files to Tomcat
You would need to copy below jar file to ${tomcat_dir}/lib in order to have the code running.

1. jasypt-1.9.2.jar

2. sqljdbc42.jar 
3. commons-dbcp-1.4.jar
4. commons-pool-1.5.4.jar
5. secured-resources-1.0.jar




STEP 7 : Datasource Injection
In the application code you may inject the datasource as below:
DataSource ds = null;
JndiTemplate jndi = new JndiTemplate();
try {
ds = jndi.lookup("java:comp/env/jdbc/yourDB", DataSource.class);
} catch (NamingException e) {
log.error("NamingException for java:comp/env/jdbc/yourDB", e);
}






JOB DONE

No comments:

Post a Comment

Flag Counter