티스토리 뷰
MySQL 5.7 버전부터 지원되기 시작한 데이터 암호화 기능은 처음에는 데이터 파일(테이블스페이스)에 대해서만 암호화 기능이 제공됐지만, MySQL 8.0으로 업그레이드되면서 데이터 파일뿐만 아니라 리두 로그나 언두 로그, 복제를 위한 바이너리 로그 등도 모두 암호화 기능을 지원하기 시작했다.
MySQL 서버의 데이터 암호화
MySQL 서버의 암호화 기능은 아래 그림과 같이 데이터베이스 서버와 디스크 사이에 데이터 읽고 쓰기 지점에서 암호화 또는 복호화를 수행하기 때문에, 디스크 입출력 이외의 부분에서는 암호화 처리가 전혀 필요하지 않다. 즉, MySQL 서버(InnoDB 스토리지 엔진)의 I/O 레이어에서만 데이터의 암호화 및 복호화 과정이 실행되는 것이다.
MySQL 서버에서 사용자의 쿼리를 처리하는 과정에서 테이블의 데이터가 암호화돼 있는지 여부를 식별할 필요가 없으며, 암호화된 테이블도 그렇지 않은 테이블과 동일한 처리 과정을 거친다.
2단계 키 관리
TDE(Transparent Data Encryption)에서 암호화 키는 키링(KeyRing) 플러그인에 의해 관리되며, MySQL 8.0 버전에서 지원되는 키링 플러그인은 다음과 같다.
- keyring_file File-Based 플러그인
- keyring_encrypted_file Keyring 플러그인 (엔터프라이즈 에디션)
- keyring_okv KMIP 플러그인 (엔터프라이즈 에디션)
- keyring_aws Amazon Web Service Keyring 플러그인 (엔터프라이즈 에디션)
다양한 플러그인이 제공되지만 마스터 키를 관리하는 방법만 다를 뿐 MySQL 서버 내부적으로 작동하는 방식은 모두 동일하며, MySQL 서버의 키링 플러그인은 2단계(2-Tier) 키 관리 방식을 사용한다.
MySQL 서버의 데이터 암호화는 마스터 키(master key)와 테이블스페이스 키(tablespace key)라는 두 가지 종류의 키를 가지고 있는데, 테이블스페이스 키는 프라이빗 키(private key)라고도 한다.
위 그림과 같이 MySQL 서버는 Vault 같은 외부 키 관리 솔루션(KMS) 또는 디스크의 파일에서 마스터 키를 가져오고, 암호화된 테이블이 생성될 때마다 해당 테이블을 위한 임의의 테이블스페이스 키를 발급한다. 그리고 MySQL 서버는 마스터 키를 이용해 테이블스페이스 키를 암호화해서 각 테이블의 데이터 파일 헤더에 저장한다. 만약 마스터 키를 변경하면, 기존의 마스터 키를 이용해 각 테이블의 테이블스페이스 키를 복호화한 다음 새로운 마스터 키로 다시 암호화 한다.
암호화와 성능
MySQL 서버의 암호화는 TDE 방식이기 때문에 디스크로부터 한 번 읽은 데이터 페이지는 복호화되어 InnoDB의 풀에 적재된다. 그래서 데이터 페이지가 한 번 메모리에 적재되면 암호화되지 않은 테이블과 동일한 성능을 보인다.
하지만 쿼리가 InnoDB 버퍼 풀에 존재하지 않는 데이터 페이지를 읽어와야 하는 경우에는 복호화 과정을 거치기 때문에 처리가 지연되고, 암호화된 테이블이 변경되면 다시 디스크로 동기화될 때 암호화돼야 하기 때문에 추가로 시간이 더 소요된다. 하지만 데이터 페이지 저장은 사용자의 쿼리를 처리하는 스레드가 아닌 백그라운드 스레드가 수행하기 때문에 실제 사용자 쿼리가 지연되는 것은 아니다.
아래 표는 암호화된 테이블과 그렇지 않은 테이블의 디스크 읽고 쓰기에 걸리는 평균 시간을 수집한 정보이다. 어느 정도 오차는 있겠지만 암호화된 테이블의 경우 읽기는 3-5배 정도 느리며, 쓰기의 경우에는 5-6배 정도 느린 것을 확인할 수 있다.
언두 로그 및 리두 로그 암호화
테이블의 암호화를 적용하더라도 디스크로 저장되는 데이터만 암호화되고 MySQL 서버의 메모리에 존재하는 데이터는 복호화된 평문으로 관리되며, 이 평문 데이터가 테이블의 데이터 파일 이외의 디스크 파일로 기록되는 경우에는 여전히 평문으로 저장된다. 때문에 테이블 암호화를 적용해도 리두 로그나 언두 로그, 복제를 위한 바이너리 로그에는 평문으로 저장되는 것이다.
InnoDB 리두 로그가 암호화됐는지는 다음과 같이 간단하게 확인할 수 있다.
mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_redo_log_encrypt';
mysql> INSERT INTO enc VALUES (2, 'hyuuny');
mysql> SET GLOBAL innodb_redo_log_encrypt=ON;
mysql> INSERT INTO enc VALUES (2, 'hyuuny');
INSERT된 레코드의 문자열이 InnoDB의 리두 로그에 보이는지만 확인해보면 된다. grep 명령을 이용한 단순한 검색 결과에서 암호화되기 전에 INSERT한 "hyuuny" 문자열은 검색되지만, 암호화 이후 INSERT된 "hyuuny" 문자열은 검색되지 않는 것을 확인할 수 있다.
## grep 명령의 결과, 문자열이 존재하면 "matches"라는 메시지를 보여준다.
## 그리고 검색한 문자열이 존재한다면 grep 명령은 반환 값으로 "0"을 리턴한다.
linux> grep 'hyuuny' ib_logfile0_ib_logfile1
Binary file ib_logfile0 matches
linux> echo $?
0
## grep 명령의 결과, 문자열이 존재하지 않으면 아무런 메시지 출력이 없다.
## 그리고 검색한 문자열이 존재하지 않으면 grep 명령은 반환 값으로 "1"을 리턴한다.
linux> grep 'hyuuny' ib_logfile0_ib_logfile1
linux> echo $?
1
바이너리 로그 암호화
테이블 암호화가 적용돼도 바이너리 로그와 릴레이 로그 파일 또한 리두 로그나 언두 로그처럼 평문을 저장한다. 일반적으로 언두 로그와 리두 로그는 길지 않은 시간 동안의 데이터만 가지기 때문에 크게 보안에 민감하지 않을 수 있지만, 바이너리 로그는 의도적으로 상당히 긴 시간 동안 보관하는 서비스도 있고 때로는 증분 백업(Incremental Backup)을 위해 바이너리 로그를 보관하기도 한다. 이런 이유로 바이너리 로그 파일의 암호화는 상황에 따라 중요도가 높아질 수 있다.
바이너리 로그와 릴레이 로그 파일 암호화 기능은 디스크에 저장된 로그 파일에 대한 암호화만 담당하고, MySQL 서버의 메모리 내부 또는 소스 서버와 레플리카 서버 간의 네트워크 구간에서 로그 데이터를 암호화하지는 않는다.
바이너리 로그 암호화 키 관리
바이너리 로그와 릴레이 로그 파일의 데이터는 파일 키(File Key)로 암호화해서 디스크로 저장하고, 파일 키는 "바이너리 로그 암호화 키"로 암호화해서 각 바이너리 로그와 릴레이 로그 파일의 헤더에 저장된다. 즉, "바이너리 로그 암호화 키"는 테이블 암호화의 마스터 키와 동일한 역할을 하며, 파일 키는 바이너리 로그와 릴레이 로그 파일 단위로 자동으로 생성되어 해당 로그 파일의 데이터 암호화에만 사용된다.
요약
- MySQL 서버의 암호화는 DB 서버와 디스크 사이의 데이터를 읽고 쓰는 지점에서 암/복호화가 이루어진다.
- 사용자의 쿼리를 처리하는 과정에서 테이블이 암호화 되어있든, 되어있지않든 동일한 처리를 하기 때문에 암호화 여부를 식별할 필요가 없다.
- 키링 플러그인의 종류는 다양하지만, MySQL 서버 내부적으로 작동하는 방식은 모두 동일하며 2단계(2-Tier) 키 관리 방식을 사용한다.
- MySQL 서버는 디스크로부터 한 번 읽어온 데이터 페이지를 복호화하여 InnoDB 풀에 적재한다.
- 테이블의 암호화를 적용하더라도 디스크로 저장되는 데이터만 암호화되고, 메모리에 존재하는 데이터는 복호화된 평문으로 관리하기 때문에 리두 로그나, 언두 로그, 복제를 위한 바이너리 로그에는 평문으로 저장된다.
- 바이너리 로그는 증분 백업이나 의도적으로 긴 시간 동안 보관하기도 함으로써, 상황에 따라 암호화의 중요도가 높아진다.
- 바이너리 로그와 릴레이 로그 파일의 데이터는 파일 키(File Key)로 암호화해서 디스크로 저장한다.
- 파일 키는 "바이너리 로그 암호화 키"로 암호화해서 각 바이너리 로그와 릴레이 로그 파일의 헤더에 저장한다.
Reference
백은빈, 이성욱. 『Real MySQL 8.0』. 위키북스, 2022
'MySQL' 카테고리의 다른 글
[MySQL] 인덱스 2 (0) | 2023.02.10 |
---|---|
[MySQL] 인덱스 1 (0) | 2023.02.04 |
[MySQL] 데이터 압축 (0) | 2023.01.30 |
[MySQL] 격리 수준 (Isolation Level) (1) | 2023.01.29 |
[MySQL] 잠금 (Lock) (1) | 2023.01.28 |
- Total
- Today
- Yesterday
- Spring
- 스프링
- 스프링 부트
- 문자열
- 노마드코더
- leetcode
- 릿코드
- Real MySQL
- 백준
- 그리디
- 스프링부트
- 리팩토링
- mysql 8.0
- 구현
- Algorithm
- webflux
- 김영한
- 알고리즘
- 북클럽
- 파이썬
- MySQL
- 자료구조
- 데이터베이스
- 정렬
- 노마드
- 코테
- kotlin
- spring boot
- 코틀린
- 인프런
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |