Jasypt: มาเข้ารหัส property value ใน properties file กัน

ปกติแล้วเวลาทำ app ขึ้นมาเราก็ต้องมีการเก็บข้อมูลจำพวก username, password ไว้ในไฟล์ properties ประมาณนี้

jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:tcp://localhost/~/appdb
jdbc.username=admin
jdbc.password=password

ซึ่งมันดูโหดมากเพราะเรากำลังเอา password ไปโชว์หร่าบน text ไฟล์กันโต้งๆ ดังนั้นถ้าข้อมูลประเภทที่ควรเป็นความลับก็ควรจะเข้ารหัสซักนิดเพื่อความปลอดภัยในชีวิตและทรัพย์สิน

สมมุติโจทย์

สมมุติโจทย์ให้ app เรา(ของเดิมนั้นแหละ) ต้องอ่านข้อมูล JDBC จาก [root]/cfg/jdbc.properties โดยให้ข้อมูลมี่เป็น password ต้องเข้ารหัสให้ไม่สามารถรู้ได้ว่า password คืออะไร

Code เดิมๆ

ถ้าเป็นแบบเดิมๆเราก็จะมี jdbc.properties เหมือนด้านบนและก็เขียน JdbcProperties ขึ้นมาเพื่ออ่าน properties ขึ้นมาประมาณนี้

package ninja.tumit.launch4jdemo.properties;

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class JdbcProperties {
 
    final static Logger log = LoggerFactory.getLogger(JdbcProperties.class);
    
    private static final String PROPERTIES_PATH = "../cfg";
    private static String PROPERTIES_FILE = "jdbc.properties";    
    private static JdbcProperties instance = null;
    private Properties prop = null;
    
    private JdbcProperties(){
        
        prop = new Properties();
        try {   
            File executorPath = new File(getExecutorPath());            
            String propertiesPath = executorPath.getParentFile().getAbsolutePath();              
            prop.load(new FileInputStream(propertiesPath+"/"+ PROPERTIES_PATH +"/" + PROPERTIES_FILE));          
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String getExecutorPath() {
        return JdbcProperties.class.getProtectionDomain().getCodeSource().getLocation().getPath();
    }      
    
    public static synchronized JdbcProperties getInstance(){
        if (instance == null)
            instance = new JdbcProperties();
        return instance;
    }

    public String getValue(String key){
        return this.prop.getProperty(key);
    }
}

ในส่วนของ app ก็เรียกมาดูว่าอ่าน properties จาก [root]/cfg/jdbc.properties

package ninja.tumit.launch4jdemo;

import java.io.IOException;

import ninja.tumit.launch4jdemo.properties.JdbcProperties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Hello Launch4j!
 *
 */
public class App 
{
    
    Logger logger = LoggerFactory.getLogger(App.class);

    public void run() {
        try {
            
            String username = JdbcProperties.getInstance().getValue("jdbc.username");
            String password = JdbcProperties.getInstance().getValue("jdbc.password");
            
            logger.info( "JDBC: username={}, password={}",  username, password);
            
            System.in.read();            
        }catch(IOException e) {
            e.printStackTrace();
        }        
    }
        
    public static void main( String[] args ) {
        new App().run();
    }
}
แน่นอนมันควรจะอ่่านได้เรียบร้อยดี

Code แบบเข้ารห้ส

เอาละมาเข้าประเด็นของหัวข้อกัน 😛

ใส่ Jasypt เข้าไปในโปรเจค

แน่นอนอย่างแรกก็ต้องเป็น lib สำหรับช่วยเข้ารหัสก่อนเราใช้ lib ที่ชื่อ Jasypt (อ่านว่าอะไรฟ่ะ) ก่อนอื่ก็เอาเข้าไปใส่ใน pom.xml ก่อน

<dependency>
	<groupId>org.jasypt</groupId>
	<artifactId>jasypt</artifactId>
	<version>1.9.2</version>
</dependency>

เข้ารหัสในไฟล์ properties

ก่อนอื่นเราต้องมีตัวช่วยในการเข้ารหัสเพื่อเอา text ที่เข้ารหัสแล้วไปแปะที่ไฟล์ properties ซึ่งเรา download ได้จากนั้น unzip ซะแล้ว cmd เข้าไปเลย
cmd-jasypt-enc.exe

  • encrypt.bat – executor ตัวช่วย generate password ที่เข้ารหัส
  • input – password เดิมๆที่เราอยากให้เป็นความลับ
  • password – ตัวปั่นให้ password เดิมๆให้อ่านยากๆ
  • —- OUTPUT —-  – นี้แหละ text ที่เราจะเอาไปแปะแทน password เดิม

ว่าแล้วก็เอาไปแปะแทน password เดิมเลย แต่เพิ่ท ENC(xxxx) หน่อยเพื่อเป็นการบอกว่า text ชุดนี้เข้ารหัสนะ

jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:tcp://localhost/~/appdb
jdbc.username=admin
jdbc.password=ENC(4pCWOkm2Kwwa1tfM0x9maZMgfMqsdw0v)

เปลี่ยนตัวอ่าน properties

จาก code เดิมเราใช้คลาส Properties ในการโหลดแต่พอเราใช้ Jasypt ก็ต้องใช้ EncryptableProperties + StandardPBEStringEncryptor แทน

package ninja.tumit.launch4jdemo.properties;

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.properties.EncryptableProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class JdbcProperties {
 
    final static Logger log = LoggerFactory.getLogger(JdbcProperties.class);
    
    private static final String PROPERTIES_PATH = "../cfg";
    private static String PROPERTIES_FILE = "jdbc.properties";    
    private static JdbcProperties instance = null;
    private Properties prop = null;
    
    private JdbcProperties(){
        
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword("MAKE_PASS");

        // prop = new Properties();
        prop = new EncryptableProperties(encryptor);        

        try {   
            File executorPath = new File(getExecutorPath());            
            String propertiesPath = executorPath.getParentFile().getAbsolutePath();              
            prop.load(new FileInputStream(propertiesPath+"/"+ PROPERTIES_PATH +"/" + PROPERTIES_FILE));          
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String getExecutorPath() {
        return JdbcProperties.class.getProtectionDomain().getCodeSource().getLocation().getPath();
    }      
    
    public static synchronized JdbcProperties getInstance(){
        if (instance == null)
            instance = new JdbcProperties();
        return instance;
    }

    public String getValue(String key){
        return this.prop.getProperty(key);
    }
}
  • StandardPBEStringEncryptor – เราจะเอา MAKE_PASS(ตัวช่วยปั่น) ใส่ไปในออปเจคของคลาสนี้
  • EncryptableProperties – เปลี่ยนจาก Properties ธรรมดาเป็นตัวนี้แล้วเอา encryptor หยอดไป

กลับไป run App อีกทีนี้เราก็จะสามารถใช้ properties ที่เข้ารหัสได้เป็นปกติเหมือนไม่เคยเข้ารหัสมาก่อน

buid ซิ buid

อ้าว! ลืมก็อปไฟล์ properties
อ้าว! ลืมก็อปไฟล์ properties
launch4j-enc
ก็อปปี้แล้ว run โอเช