250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- 제이쿼리그리드
- 보안
- MessageQueue
- ci/cd
- jqGrid
- spring
- 대용량 업로드
- ORM
- DevOps
- apache.poi
- stream api
- 자바8
- mssql
- Jenkins
- docker
- Javascript
- 자동빌드
- 엑셀 업로드
- QueryDSL
- JQuery
- mom
- Stream
- 스트림
- java
- 그리드
- sqlserver
- rabbitmq
- JPA
- poi
- 자동배포
Archives
- Today
- Total
개발 메모장
[보안] Jasypt를 이용한 민감정보 암호화 본문
728x90
- 개인정보가 포함된 서비스의 소스에 DB접근 정보가 평문화 되어있어 암호화를 해야 했습니다.
- 검색하여 찾아본 결과 모든 내용이 Jasypt라는 라이브러리를 이용해 처리하고 있음을 확인하고 적용했습니다.
- Spring-boot로 새로 만들어 처리할 때에는 아주 쉽게 처리가 가능했으나 해당 서비스는 기존에 설정된 것들이 복잡하게 얽혀있어 처리에 애를 먹었습니다.
- 적용하는 과정에서의 방법에 대해 공유하고자 합니다.
#. Jasypt의 특징
- 암호화 및 복호화
- 비밀번호, DB 연결 정보 및 기타 기밀 정보와 같은 민감한 데이터를 암호화하고 복호화가 가능합니다. - 다양한 암호화 알고리즘 지원
- AES, DES, Triple DES, PBE 등을 포함한 다양한 암호화 알고리즘을 지원합니다.
- 개발자는 보안 요구 사항에 따라 가장 적합한 암호화 알고리즘을 선택할 수 있습니다. - Java 애플리케이션과의 통합
- 웹 애플리케이션, 데스크톱 애플리케이션 및 백엔드 시스템을 포함한 Java 애플리케이션에 쉽게 통합될 수 있습니다. - 사용 용이성
- 암호화 및 복호화 프로세스의 복잡성을 추상화하여 개발자가 낮은 수준의 암호화 세부 사항을 처리하지 않고도 보안 기능 구현에 집중할 수 있도록 합니다. - 구성 기반 암호화
- 개발자는 간단한 구성 설정을 사용하여 데이터베이스 비밀번호와 같은 민감한 구성 속성을 암호화할 수 있습니다.
- 코드를 크게 변경하지 않고도 구성 파일에 저장된 중요한 정보를 보호하는 데 도움이 됩니다. - 명령줄 인터페이스
- 사용자가 콘솔에서 데이터를 암호화하고 해독할 수 있는 CLI 도구를 제공합니다.
#. Jasypt가 제공하는 암호화 알고리즘
- PBEWithMD5AndDES
- MD5 다이제스트 및 DES 대칭 암호화를 사용합니다. - PBEWithMD5AndTripleDES
- PBEWithMD5AndDES과 비슷하지만 더 강력한 암호화를 위해 Triple DES(3DES)를 사용합니다. - PBEWithSHA1AndDESede
- SHA-1 다이제스트와 Triple DES를 결합합니다. - PBEWithSHA1AndRC2_40
- 40바이트 키와 함께 SHA-1 다이제스트 및 RC2를 사용합니다. - PBEWithSHA1AndRC4_128
- SHA-1 다이제스트와 RC4를 128바이트 키와 결합합니다. - PBEWithSHAAnd128BitAES-CBC-BC/PKCS5Padding
- CBC 모드에서 128바이트 키와 함께 SHA 다이제스트 및 AES를 사용합니다. - PBEWithSHAAnd256BitAES-CBC-BC/PKCS5Padding
- PBEWithSHAAnd128BitAES-CBC-BC/PKCS5Padding와 비슷하지만 256바이트 AES 키를 사용합니다.
#. 암호화 및 복호화 CLI 사용방법
- http://www.jasypt.org/download.html 클릭합니다.
- 라이브러리 버전에 맞게 다운로드 후 압축해제하기
- cmd > 압축 푼 경로 및 bin폴더까지 접속
- 전체 경로 중 띄어쓰기가 포함된 폴더 등의 경로가 있다면
"오류: 기본 클래스 org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI을(를) 찾거나 로드할 수 없습니다."가 발생합니다. - encrypt inpuit="암호화할내용" password="소금값" algorithm="PBEWITHMD5ANDDES"으로 작성
복호화의 경우 encrypt -> decrypt로 변경하여 처리 - 암호화 처리

- 복호화 처리

#. 적용방법
- xml에 DB정보를 저장해 놓은 상태였으나 xml에 jasypt를 bean으로 설정하고 그 경로를 xml파일로 잡게 되면 제대로 처리가 되지 않았습니다.
- xml 파일의 DOCTYPE이 NULL이고 jasypt의 configurationEncryptor bean의 프로퍼티 name은 config라 맞지 않음 -> DOCTYPE을 config로 맞추면 .metadata에 dtd 파일이 없다 -> dtd파일을 강제로 넣어주면 값을 읽을 수 없다.
같은 내용의 오류가 발생하였습니다. - 그리하여 xml에 직접 연결하여 처리하는 방법에 한계를 느껴 xml에 있는 DB정보만 properties 파일 옮겨 처리하는 방법을 택하였습니다.
- 기존 설정 자체가 xml 파일 기준으로 되어있어 삽질도 많이 했습니다.
1. 라이브러리 추가(maven)
- spring 3.1 이후 버전의 호환 및 지원 가능합니다.
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt-spring31</artifactId>
<version>1.9.3</version>
</dependency>
2. context.xml 내 bean 설정
- 우선 기존의 xml 내용을 간략히 살펴보면 placeholder의 경로는 이미 잡혀있는 상태입니다.
- context.xml의 내용입니다.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<bean class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
<property name="configurations">
<list>
<ref bean="configuration" />
</list>
</property>
</bean>
</property>
</bean>
<bean id="configuration" class="org.apache.commons.configuration.CompositeConfiguration">
<constructor-arg>
<list>
<bean class="org.apache.commons.configuration.XMLConfiguration">
<constructor-arg type="java.lang.String">
<value>config/config.xml</value>
</constructor-arg>
</bean>
</list>
</constructor-arg>
</bean>
<bean id="dataSourceSpied" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
- DB정보가 담긴 config.xml 입니다.
<?xml version="1.0" encoding="UTF-8"?>
<config>
<jdbc>
<driverName>com.microsoft.sqlserver.jdbc.SQLServerDriver</driverName>
<url>DB URL</url>
<id>접근 계정</id>
<pw>접근 암호</pw>
</jdbc>
</config>
- jasypt 사용 시에도 placeholder를 지정하게끔 되어있습니다.
- placeholder가 2개가 되면 오류가 발생했고, 또한 기존과 같이 configUtil을 이용해 값은 뽑아낼 수 있어야 했으며 dataSource bean에 변수로 들어가는 값이 db.properties를 봐야 했기에 기존 bean인 PropertyPlaceholderConfigurer 삭제하였습니다.
- placeholder는 중복으로 사용이 불가한 것 같습니다.
<bean id="propertyConfigurer" class="org.jasypt.spring31.properties.EncryptablePropertyPlaceholderConfigurer">
<constructor-arg ref="configurationEncryptor" />
<property name="location" value="classpath:config/db.properties" />
</bean>
<bean id="environmentVariablesConfiguration" class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndDES"/>
<!-- <property name="password" value="test"/> -->
<property name="passwordEnvName" value="JASYPT_PASSWORD"/>
</bean>
<bean id="configurationEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="config" ref="environmentVariablesConfiguration"/>
</bean>
- 여기서 한 가지 주목하실 점은 password입니다.
- 테스트하실 때에는 password에 작성하셔도 되지만 솔트값이 노출되면 복호화가 가능하기에 운영서버 적용 시에는 OS 환경변수에서 지정하고 그 환경변수를 호출해 처리해줘야 합니다.
- 환경변수는 property name을 passwordEnvName으로 설정해야 합니다.(password로 설정하고 값을 넣으면 읽어오지 못합니다.)
3. 환경변수 설정
- 환경변수 설정은 간단하며 아래와 같이 설정하면 됩니다.

4. properties에 적용하기
- properties를 생성하여 아래와 같이 property를 작성하고, 그 값은 상단에 명시한 방법을 통해 암호화한 값을 넣어줍니다.
- 값을 넣을 땐 ENC() 내에 넣어야 복호화가 정상적으로 처리되어 DB와 연결됩니다.
- Jasypt의 placeholder에서 새로 지정한 경로의 db.properties입니다.
database.driverName=com.microsoft.sqlserver.jdbc.SQLServerDriver
database.url=ENC(CtVN5KhL0CFmGrx/T9YHD/n29uOe1eXQIv5JXQRR+UIL3amV7I9xX25G5SPoTkv+OuzWFihD6Gtsaccl8L0KN3iCfPbwMTR+)
database.username=ENC(4/F4Xv+HsFtXoFjZRgZevacKsjpMn0E6)
database.password=ENC(bof7zWWSWW1fwFNUVkK/J/WtmXoZ6/Hc)
jasypt.encryptor.password=${JASYPT_PASSWORD}
- 또한 properties에서 database를 사용해 url, username, password를 처리했으므로 context.xml의 dataSourceSpied bean의 파라미터가 값도 {jdbc.url}이 아닌 {database.url}과 같이 수정해줘야 합니다.
- 이렇게 적용하면 DB에 접근이 가능합니다.
5. 로직 내 사용방법
- 로직 내에서 운영 / 개발서버에 따라 로직이 달라지게 되는 경우 서버 값을 가져와 사용해야 합니다.
- 그러나 암호화가 되어있기에 복호화 처리를 해줘야 하므로 복호화 로직을 구현합니다.
- 대략적인 호출방법은 아래와 같습니다.
import org.springframework.core.env.Environment;
@Controller
@PropertySource({"classpath:/config/dbConfig.properties"})
public class TestController {
@Autowired
private Environment env;
@RequestMapping("/test.do")
public void test123() {
String server = decryptDbProperty(env.getProperty("data.url"), env.getProperty("jasypt.encryptor.password"))
}
public static String decryptDbProperty(String key, String pw) {
StandardPBEStringEncryptor enc = new StandardPBEStringEncryptor();
enc.setAlgorithm("PBEWithMD5AndDES");
enc.setPassword(pw);
if(key.startsWith("ENC(") && key.endsWith(")")) {
key = key.substring(4, key.length() -1);
}
return enc.decrypt(key);
}
}
- @PropertySource를 이용해 외부 프로퍼티를 호출합니다.
- 호출한 데이터를 변수에 담는 방법은 @Value를 사용해도 되고 위처럼 Environment의 getProperty를 사용해도 됩니다.
- 복호화 시엔 알고리즘과 솔트값인 password 등 지정한 세팅값과 동일하게 세팅해줘야 하며 ENC()를 그대로 호출해 오기 때문에 ENC()를 제거하는 작업이 필요하니 참고해 주시길 바랍니다.
#. Spring Boot에서는 아래와 같이 처리했고 위 내용과 중첩되는 내용이 많아 간략하게만 설명하도록 하겠습니다.
1. 라이브러리 추가
implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5'
2. Config 파일 생성
@Configuration
@EnableEncryptableProperties
public class JasyptAESConfig {
@Bean("jasyptEncryptorAES")
public StringEncryptor stringEncryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword("tester"); // 소금값
config.setAlgorithm("PBEWITHMD5ANDDES"); // 알고리즘
config.setKeyObtentionIterations("1000"); // 해싱 반복횟수
config.setPoolSize("1"); // 풀 크기
config.setProviderName("SunJCE"); // 제공자명
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); // 소금값 생성 클래스 설정
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator"); // 벡터 생성 클래스 설정
config.setStringOutputType("base64"); // 인코딩 출력 타입
encryptor.setConfig(config);
return encryptor;
}
}
3. 테스트 파일 생성하여 암호화(CLI -> 소스 내 코드로 대체)
class JasyptAESConfigTest {
@Test
void testStringEncryptor() {
String url = "암호화 할 url";
String username = "암호화 할 유저명";
String password = "암호화 할 비밀번호";
System.out.println("===========> " + jasyptEncoding(url));
System.out.println("===========> " + jasyptEncoding(username));
System.out.println("===========> " + jasyptEncoding(password));
}
public String jasyptEncoding(String value) {
String key = "tester"; // 소금값
StandardPBEStringEncryptor pbeEnc = new StandardPBEStringEncryptor();
pbeEnc.setAlgorithm("PBEWITHMD5ANDDES");
pbeEnc.setPassword(key);
pbeEnc.setIvGenerator(new RandomIvGenerator());
return pbeEnc.encrypt(value);
}
}

4. properties 적용
spring.datasource.url=ENC(7JINp6zuKJ3I4S2hsUaPgaqwx5BCwmwjE88x1bQxCnqQLaD5NbZXWHDeUVUfeQp8IbzjJHIGIhR2xtS5BZNuSbOj5JQUFcfn)
spring.datasource.username=ENC(VTIl4ruh6p37XNcLTa2qAUpV/2pD5LcK)
spring.datasource.password=ENC(GibMit23TKHr80Aw/vPUa/roiXkgO8pq)
spring.datasource.driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
jasypt.encryptor.password=tester // 소금값
jasypt.encryptor.bean=jasyptEncryptorAES // bean 값
===========================================================
틀린 내용이 있거나 이견 있으시면 언제든 가감 없이 말씀 부탁드립니다!
===========================================================
728x90
'보안' 카테고리의 다른 글
[보안] Ajax 요청에 대한 응답값 변조, 프록시 조작 및 우회 방지 (0) | 2024.12.24 |
---|---|
[CDN] cdn.jsdelivr.net SSL 만료(부트스트랩 오류) (0) | 2024.05.03 |
[보안] Replay Attack 정의 및 대응 (0) | 2024.01.16 |
[보안] 시큐어 코딩이란? (1) | 2023.12.18 |
[보안] 파일 업로드 시 취약점 사전 대응 방법(Java, Windows) (1) | 2023.11.30 |