[Spring] Jasypt로 민감한 정보 암호화해 관리하기

이번 프로젝트에서 민감한 설정(DB 계정, 비밀번호 등)을 private 레포지토리에 저장하고 이 레포지토리를 서브모듈로 사용해 관리하고 있었다.

하지만 서브모듈을 사용하는데에 다음과 같은 많은 번거로움이 있었다.

  1. 서브모듈을 포함하고 있는 부모 레포지토리는 서브모듈로 적용한 레포지토리의 커밋 내역을 참조하고 있기 때문에 서브모듈에 수정사항이 생겼다면 꼭 서브모듈 먼저 push 후 메인 프로젝트를 push 하여야 한다.
  2. 서브모듈로 적용한 레포지토리에 변경사항이 부모 레포지토리에 바로 반영되지 않으므로 서브모듈을 사용하는 프로젝트를 관리할 때 서브모듈의 상태를 항상 확인하고 변경사항이 있다면 update 해주어야만 반영된다.

이러한 주의점들을 지켜주지 않으면 프로젝트를 진행하면서 서브모듈의 이전 커밋 내역을 들고와 설정이 꼬여 에러가 발생하는 일들이 생기기도 했다.

그리고 push 시에 순서에 의존한다는 문제와 변경내역을 계속확인해주어야 하는 등 번거로운 부분들이 있어 다른 수단을 찾아보기로 했다.


Jasypt

그렇게 찾게 된 것은 바로 Jasypt 라이브러리였다.

공식문서에 들어가보면 "Spring Boot 애플리케이션의 속성 값에 대한 암호화 지원을 제공합니다." 라고 한다.

https://github.com/ulisesbocchio/jasypt-spring-boot#use-you-own-custom-encryptor

 

GitHub - ulisesbocchio/jasypt-spring-boot: Jasypt integration for Spring boot

Jasypt integration for Spring boot. Contribute to ulisesbocchio/jasypt-spring-boot development by creating an account on GitHub.

github.com

이제 두 개의 레포지토리를 사용(서브모듈)하는 것이 아닌 메인 레포지토리에서 민감한 설정들을 암호화해 관리해보자


Jasypt 적용하기

아래 설명대로 따라한다면 아주 쉽게 Jasypt를 적용할 수 있다. 👍🏻

 

1. build.gradle에 jasypt 의존성 추가
implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5'

스프링 부트 3.3.3 버전을 기준으로 jasypt 라이브러리 3.0.4 버전을 추가하면 호환이 안되는 문제가 있는데

현재 기준(2024.09.15) 최신 버전을 추가해 해결하였다.

 

2. jasypt 설정 코드 추가

공식문서에 있는 default 설정을 그대로 가져왔다.

@Configuration
public class JasyptConfig {

    @Value("${jasypt.encryptor.password}")
    private String password;

    @Bean("jasyptStringEncryptor")
    public StringEncryptor stringEncryptor() {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); // Jasypt에서 제공하는 스레드 풀을 사용하는 암호화 클래스 
        SimpleStringPBEConfig config = new SimpleStringPBEConfig(); // PBE(Password Based Encryption - 비밀번호 기반 암호화 설정을 위한 클래스
        config.setPassword(password); // 암호화에 사용될 비밀번호를 설정
        config.setAlgorithm("PBEWithMD5AndDES"); // 사용할 암호화 알고리즘을 설정
        config.setKeyObtentionIterations("1000"); // 키 생성 시 해싱 반복 횟수 설정. (높을수록 보안은 강화되지만 성능은 저하될 수 있다)
        config.setPoolSize("1"); // 스레드 풀 크기 설정 (애플리케이션 머신의 코어 수와 동일하게 설정하는 것을 권장)
        config.setProviderName("SunJCE"); // 사용할 프로바이더 이름을 설정 (Java의 표준 암호화 제공자)
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); // Salt 생성에 사용할 클래스를 지정 (RandomSaltGenerator는 무작위 salt를 생성)
        config.setIvGeneratorClassName("org.jasypt.iv.NoIvGenerator"); // 초기화 벡터(IV) 생성에 사용할 클래스를 지정 (NoIvGenerator는 IV를 사용하지 않음을 의미)
        config.setStringOutputType("base64"); // 암호화된 결과물의 출력 형식
        encryptor.setConfig(config); // 위에서 설정한 SimpleStringPBEConfig의 설정들을 encryptor 객체에 적용
        return encryptor;
    }
}

 

3. 암호화하기

 

https://www.devglan.com/online-tools/jasypt-online-encryption-decryption

 

Jasypt Encryption and Decryption Online

Jasypt online free tool for encryption and decryption.This tool supports one way and two way password encryptor using Jasypt as well as matching encrypted password using Jasypt.

www.devglan.com

위 사이트에서 다음과 같이 암호화 복호화를 할 수 있다.

비밀번호 "1234"를 암호화 위 사이트를 통해 암호화 해보자.

이제 평문을 암호화된 데이터로 바꾸주자.

이때 중요한점이 ENC()로 암호화된 데이터를 감싸 암호화 되었다는 표시를 해주어야

애플리케이션이 실행될 때 암호화된 데이터라는 것을 인지해 복호화해 설정을 구성하고 실행할 수 있다.

 

4. 비밀번호 주입

설정에 비밀번호를 작성하면 push 되었을 때 비밀번호가 노출되기 때문에 암호화의 의미가 없다.

그래서 비밀번호를 환경변수로 주입해 노출되지 않도록 할 것이다.

 

지금은 로컬에서 실행해 테스트할 것임으로 아래와 같이 실행 환경 변수에 비밀번호를 추가해줬다.

 

 

5. 애플리케이션 실행

위에서 설명한 설정과는 다르지만 로컬에서 실행해 h2 DB 주소로 변경하고 url을 암호화해

환경변수로 넣어준 비밀번호로 정상적으로 복호화되는지 확인했다.

 

만약 주소를 망가트린다면 ? ENC()로 감싸지 않고 아래와 같이 실행해봤다.

암호화된 값이라는 것을 인식못해 복호화를 하지 않아 이상한 DB 주소로 연결을 하려해 오류가 발생한 모습이다.

ENC()로 감싸는 거 귀찮더라도 잊지 말자...

인텔리제이에 jasypt 암복호화를 단축키로 해주는 플러그인들이 있는데 현재 제대로 작동하지 않는 것 같다

좋은 플러그인이나 괜찮은 방법이 생긴다면 추가로 포스팅할 예정이다.