군만두의 IT 공부 일지

[스터디] 11. 데이터 암호화 본문

학습일지/데이터베이스

[스터디] 11. 데이터 암호화

mandus 2024. 10. 10. 12:33

 
이번에는 7장 데이터의 암호화에 대해서 공부하려고 합니다.
 

  • MySQL 5.7 버전: 데이터 파일(테이블스페이스)에 대해서 암호화 기능이 제공됨.
  • MySQL 8.0 버전: 리두 로그, 언두 로그, 바이너리 로그 등도 암호화 기능이 제공됨. 

* 테이블스페이스: 데이터 파일들이 모여 있는 집합. 즉, 하나 또는 여러 개의 데이터 파일로 구성되어 있는 논리적인 데이터 저장 구조.

7.1 MySQL 서버의 데이터 암호화

  • 데이터베이스 서버와 디스크 사이의 데이터 읽고 쓰기 지점(InnoDB 스토리지 엔진의 I/O 레이어)에서만 암호화 또는 복호화를 수행함.
  • 사용자 쿼리를 처리하는 과정에서 데이터 암호화 여부를 식별할 필요가 없는 암호화 방식을 TDE(Transparent Data Encryption) 또는 Data at Rest Encryption이라고 함.

▲ MySQL 서버의 디스크 입출력

* TDE: 투명한 데이터 암호화(Transparent Data Encryption)

* Data at Rest: 저장 데이터. 메모리(In-Process)나 네트워크 전송(In-Transit) 단계가 아닌 디스크에 저장(At Rest)된 단계에서만 암호화됨.

7.1.1 2단계 키 관리


  • 키링(KeyRing) 플러그인에 의해 관리됨.
    • 커뮤니티 에디션에서는 keyring_file 플러그인만 사용 가능함.
    • 마스터 키를 관리하는 방법만 다르고 MySQL 서버 내부에서 작동하는 방식은 동일함.
  • MySQL 서버의 키링 플러그인은 2단계(2-Tier) 키 관리 방식을 사용함.
    • 데이터 암호화는 마스터 키(master key)테이블스페이스 키(tablespace key, 또는 private key)를 가지고 있음.
    • (1) HashiCorp 같은 외부 키 관리 솔루션(KMS, Key Management Service) 또는 디스크의 파일(keyring_file 또는 keyring_encrypted_file 플러그인 사용 시)에서 마스터 키를 가져옴.
    • (2) 암호화된 테이블이 생성될 때마다 해당 테이블을 위한 임의의 테이블스페이스 키를 발급함.
    • (3) MySQL 서버는 마스터 키를 이용해 테이블스페이스 키를 암호화해서 각 테이블의 데이터 파일 헤더에 저장함.
      • 생성된 테이블스페이스 키는 테이블이 삭제되지 않는 이상 변경되지 않음. 서버 외부에 노출되지 않음.
      • 마스터 키는 외부의 파일을 이용하기 때문에 주기적으로 변경해야 함.
--- MySQL 서버의 마스터 키 변경
mysql> ALTER INSTANCE INNODB MASTER KEY;
  • MySQL 서버에서 2단계 암호화 방식을 사용하는 이유는 암호화 키 변경으로 인한 과도한 시스템 부하를 피하기 위해서임.
  • MySQL 서버의 TDE에서 지원되는 암호화 알고리즘은 AES 256비트이며, 이외의 알고리즘은 지원되지 않음.
    • 테이블스페이스 키: AES-256 ECB(Electronic CodeBook) 알고리즘으로 암호화
    • 실제 데이터 파일: AES-256 CBC(Cipher Block Chaining) 알고리즘으로 암호화

* AES: 고급 암호화 표준(Advanced Encryption Standard)

 

▲ 2단계 암호화 아키텍처

7.1.2 암호화와 성능


  • TED 방식이기 때문에 디스크로부터 한 번 읽은 데이터 페이지는 복호화되어 InnoDB 버퍼 풀에 적재됨.
    • 데이터 페이지가 한 번 메모리에 적재되면 암호화되지 않은 테이블과 동일한 성능을 보임.
    • 적재되지 않은 데이터 페이지를 읽는 경우에는 복호화 과정을 거쳐야 하므로 쿼리 처리가 지연.
    • 데이터 페이지 저장은 MySQL 서버의 백그라운드 스레드가 수행하기 때문에 실제 사용자 쿼리가 지연되지 않음.
  • AES 암호화 알고리즘은 암호화하고자 하는 평문의 길이가 짧은 경우, 암호화 키의 크기에 따라 암호화된 결과의 용량이 더 커질 수도 있음. 이미 데이터 페이지는 암호화 키보다 훨씬 크기 때문에 암호화 결과가 평문의 결과와 동일한 크기의 암호문을 반환함.
    • 따라서 TED를 적용해도 데이터 파일의 크기는 암호화되지 않은 테이블과 동일한 크기를 가짐.(InnoDB 버퍼 풀의 효율이나 메모리 사용 효율이 떨어지지 않음.)
  • 같은 테이블에 대해 암호화와 압축이 동시에 적용되면, MySQL 서버는 압축을 먼저 실행하고 암호화를 적용함.
    • 암호화된 테이블의 데이터 페이지는 복호화된 상태로 InnoDB 버퍼 풀에 저장되지만, 압축된 데이터 페이지는 압축 또는 압축 해제의 모든 상태로 InnoDB 버퍼 풀에 존재할 수 있기 때문에 매번 암복호화 작업을 수행하는 것을 방지하기 위함.

--- 암호화된 테이블의 읽고 쓰기 성능 확인
mysql>  SELECT (SUM(SUM_TIMER_READ) / SUM(COUNT_READ))/1000000000 as avg_read_latency_ms,
		(SUM(SUM_TIMER_WRITE) / SUM(COUNT_WRITE))/1000000000 as avg_write_latency_ms
        FROM performance_schema.file_summary_by_instance
        WHERE file_name LIKE '%DB_NAME/TABLE_NAME%';

7.1.3 암호화와 복제


  • 소스 서버와 레플리카 서버는 서로 각자의 마스터 키와 테이블스페이스 키를 관리하기 때문에, 복제 멤버들의 데이터 파일은 암호화 되기 전의 값이 동일하더라도 실제 암호화된 데이터가 저장된 데이터 파일의 내용은 다름.
    • MySQL 서버에서 기본적으로 모든 노드는 각자의 마스터 키를 할당해야 함.
    • 마스터 키 자체가 레플리카로 복제되지 않기 때문에 테이블스페이스 키 또한 레플리카로 복제되지 않음. 
  • 마스터 키 로테이션을 실행하면 소스 서버레플리카 서버가 각각 서로 다른 마스터 키를 새로 발급받음.
    • MySQL 서버의 백업에서 TED의 키링(Key Ring) 파일을 백업하지 않는 경우가 있는데, 키링 파일을 찾지 못하면 데이터 복구를 할 수 없음.
    • 키링 파일을 데이터 백업과 별도로 백업한다면, 마스터 키 로테이션 명령으로 TDE의 마스터 키가 언제 변경됐는지까지 기억해야 함.
--- 복제 소스 서버의 마스터 키 변경
mysql> ALTER INSTANCE ROTATE INNODB MASTER KEY;

7.2 keyring_file 플러그인 설치

  • MySQL 커뮤니티 에디션에서는 keyring_file 플러그인만 가능함.
  • keyring_file 플러그인은 테이블스페이스 키를 암호화하기 위한 마스터 키를 디스크 파일로 관리함. 마스터 키는 평문으로 디스크에 저장되기 때문에 외부에 노출된다면 데이터 암호화가 무용지물임.
  • TDE 플러그인의 경우 MySQL 서버가 시작되는 단계에서도 가장 빨리 초기화돼야 함.
    • (1) 설정 파일(my.cnf)에서 early-plugin-load 시스템 변수에 keyring 플러그인을 위한 라이브러리(keyring_file.so)를 명시함.
    • (2) keyring_file 플러그인이 마스터 키를 저장할 키링 파일의 경로를 keyring_file_data 설정에 명시함. 하나의 MySQL 서버만 참조해야 함.
 early-plugin-load = keyring_file.so
 keyring_file_data = /very/secure/directory/tde_master.key
  • (3) MySQL 서버를 재시작하면 자동으로 keyring_file 플러그인이 초기화됨.
--- keyring_file 플러그인의 초기화 여부 확인
mysql> SHOW PLUGINS;
  • (4) MySQL 서버는 플러그인의 초기화와 동시에 keyring_file_data 시스템 변수의 경로에 빈 파일을 생성함. 데이터 암호화 기능을 사용하는 테이블을 생성하거나 마스터 로테이션을 실행하면 키링 파일의 마스터 키가 초기화됨.
linux> ls -alh tde_master.key
-rw-r----- 1 matt 0B 7 27 14:24 tde_master.key

mysql> ALTER INSTANCE ROTATE INNODB MASTER KEY;

linux> ls -alh tde_master.key
-rw-r----- 1 matt 187B 7 27 14:24 tde_master.key

7.3 테이블 암호화

  • 키링 플러그인은 마스터 키를 생성하고 관리하는 부분까지만 담당하기 때문에 암호화된 테이블을 생성하고 활용하는 방법은 모든 키링 플러그인이 동일함.

7.3.1 테이블 생성


  • ENCRYPTION='Y' 옵션만 추가하면 테이블의 데이터가 디스크에 기록될 때는 데이터가 자동으로 암호화되어 저장되고, 다시 디스크에서 메모리로 읽어올 때 복호화됨.
 mysql> CREATE TABLE tab_encrypted (
 	id INT,
    	data VARCHAR(100),
    	PRIMARY KEY(id)
    	) ENCRYPTION='Y';

mysql> INSERT INTO tab_encrypted VALUES (1, 'test_data');

mysql> SELECT * FROM tab_encrypted;
  • 암호화된 테이블만 검색할 때는 information_schema의 TABLES 뷰를 이용함.
mysql> SELECT table_schema, table_name, create_options
	FROM information_schema.tables
    	WHERE table_name='tab_encrypted';
  • 모든 테이블에 대해 암호화를 적용하려면 default_table_encryption 시스템 변수를 ON으로 설정하면, ENCRYPTION 옵션을 별도로 설정하지 않아도 됨.

7.3.2 응용 프로그램 암호화와의 비교


  •  응용 프로그램에서 직접 암호화해서 MySQL 서버에 저장하는 경우, 칼럼의 값이 이미 암호화된 것인지 여부를 MySQL 서버가 인지하지 못함.
    • 해당 칼럼은 인덱스를 생성하더라도 인덱스의 기능을 100% 활용할 수 없음.
  • MySQL 서버의 TED 암호화는 실행 중인 MySQL 서버에 로그인하면 모든 데이터를 확인할 수 있음.
  • 응용 프로그램 암호화는 MySQL 서버에 로그인할 수 있다고 하더라도 평문의 내용을 확인할 수 없음.

7.3.3 테이블스페이스 이동


  • 테이블을 다른 서버로 복사해야 하는 경우 또는 특정 테이블의 데이터 파일만 백업했다가 복구하는 경우에 레코드를 덤프했다가 복구하는 방식보다 훨씩 효율적이고 빠름.
--- 테이블스페이스 익스포트(Export)
mysql> FLUSH TABLES cource_table FOR EXPORT;
  • 암호화되지 않은 테이블의 테이블스페이스 복사 과정
    • (1) FLUSH TABLES 명령으로 MySQL 서버는 source_table의 저장되지 않은 변경 사항을 모두 디스크로 기록함.
    • (2) 더이상 source_table에 접근할 수 없게 잠금을 거는 것과 동시에 source_table의 구조를 source_table.cfg 파일로 기록함.
    • (3) source_table.ibd 파일과 source_table.cfg 파일을 목적지 서버로 복사함.
    • (4) UNLOCK TABLES 명령을 실행해 source_table을 사용할 수 있게 함.
  • 암호화된 테이블의 테이블스페이스 복사 과정
    • (1) FLUSH TABLES 명령으로 MySQL는 임시로 사용할 마스터 키를 발급해서 source_table.cfp라는 파일로 기록함.
    • (2) 암호화된 테이블의 테이블스페이스 키를 기존 마스터 키로 복호화하고, 임시로 발급한 마스터 키를 이용해 다시 암호화해서 데이터 파일의 헤더 부분에 저장함.
    • 암호화된 테이블에서 테이블스페이스 이동기능을 사용할 때는 데이터 파일과 임시 마스터 키가 저장된 *.cfp 파일을 함께 복사해야 함. 

7.4 언두 로그 및 리두 로그 암호화

  • 테이블의 암호화를 적용하면 디스크로 저장되는 데이터만 암호화되고, MySQL 서버의 메모리에 존재하는 데이터는 복호화된 평문으로 관리되고, 이 평문 데이터가 테이블의 데이터 파일외 디스크 파일로 기록되는 경우에 평문으로 저장됨.
    • 리두 로그, 언두 로그는 평문으로 저장됨.
    • MySQL 8.0.16 버전부터는 innodb_redo_log_encrypt 시스템 변수와 innodb_undo_encrypt 시스템 변수를 이용해 InnoDB 스토리지 엔진의 리두 로그와 언두 로그를 암호화된 상태로 저장할 수 있음.

* 리두 로그: 데이터베이스에서 성공적으로 커밋된 변경 사항을 다시 적용하기 위해 사용하는 로그

* 언두 로그: 데이터베이스에서 아직 커밋되지 않은 변경 사항을 되돌리기 위해 사용하는 로그

 

  • 실행 중인 MySQL 서버에서 언두 로그나 리두 로그를 활성화해도 모든 리두 로그나 언두 로그의 데이터를 해당 시점에 한번에 암호화해서 다시 저장할 수 없음.
    • 리두 로그나 언두 로그를 평문으로 저장하다가 암호화가 활성화되면, 그때부터 생성되는 리두 로그나 언두 로그만 암호화해서 저장함.
    • 리두 로그와 언두 로그가 암호화되는 상태에서 암호화를 비활성화하면, 그때부터 저장되는 로그만 평문으로 저장함.
  • 리두 로그와 언두 로그 데이터 모두 각각의 테이블스페이스 키로 암호화되고, 테이블스페이스 키는 다시 마스터 키로 암호화되어 리두 로그 파일과 언두 로그 파일의 헤더에 저장됨.
    • 이 테이블스페이스 키는 실제 테이블의 암호화에 사용된 테이블스페이스 키가 아니라, 리두 로그와 언두 로그 파일을 위한 프라이빗 키를 의미함.
--- InnoDB 리두 로그가 암호화됐는지 확인
mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_redo_log_encrypt';

mysql> INSERT INTO enc VALUES (2,'Real-MySQL');

mysql> SET GLOBAL innodb_redo_log_encrypt=ON;
mysql> INSERT INTO enc VALUES (2,'Real-MongoDB');
--- INSERT된 레코드의 문자열이 InnoDB의 리두 로그에 보이는지 확인
--- 암호화되기 전에 INSERT한 “Real-MySQL” 문자열은 검색되지만,
--- 암호화 이후 INSERT된 “Real-MongoDB” 문자열은 검색되지 않음.

## grep 명령의 결과, 문자열이 존재하면 "matches"라는 메시지를 출력함.
## 검색한 문자열이 존재한다면 grep 명령은 "0"을 반환함.
linux> grep 'Real-MySQL' ib_logfile0 ib_logfile1
Binary file ib_logfile0 matches
linux> echo $?
0

## grep 명령의 결과, 문자열이 존재하지 않으면 메시지를 출력하지 않음.
## 검색한 문자열이 존재하지 않으면 grep 명령은 "1"을 반환함.
linux> grep 'Real-MongoDB' ib_logfile0 ib_logfile1
linux> echo $?
1

7.5 바이너리 로그 암호화

  • 테이블 암호화를 적용하면 바이너리 로그와 릴레이 로그 파일도 평문으로 저장됨.
    • 바이너리 로그의 암호화는 장기간 보관하는 서비스도 있고, 증분 백업을 위해 보관하기도 하므로 중요도가 높음.

* 바이너리 로그: MySQL 서버에서 발생한 모든 데이터 변경 작업을 기록하는 로그

* 릴레이 로그: MySQL 복제 환경에서 슬레이브 서버가 마스터 서버로부터 받은 바이너리 로그의 내용을 임시로 저장한 로그

 

  • 바이너리 로그와 릴레이 로그 파일 암호화 기능은 디스크에 저장된 로그 파일에 대한 암호화만 담당함.
    • MySQL 서버의 메모리 내부 또는 소스 서버와 레플리카 서버 간의 네트워크 구간에서 로그 데이터를 암호화하지 않음.
    • 복제 멤버 간 네트워크 구간에서 바이너리 로그를 암호화하려면 MySQL 복제를 위한 계정이 SSL을 사용하도록 설정함.

7.5.1 바이너리 로그 암호화 키 관리


  • 바이너리 로그와 릴레이 로그 파일의 데이터는 파일 키(File Key)로 암호화해서 디스크로 저장하고, 파일 키는 바이너리 로그 암호화 키로 암호화해서 각 바이너리 로그와 릴레이 로그 파일의 헤더에 저장됨.
  • 바이너리 로그 암호화 키는 테이블 암호화의 마스터 키와 동일한 역할을 하며, 파일 키는 바이너리 로그와 릴레이 로그 파일 단위로 자동으로 생성되어 해당 로그 파일의 데이터 암호화에만 사용됨.

* 파일 키: 각바이너리 로그와 릴레이 로그 파일에 대해 고유하게 생성되는 키

* 바이너리 로그 암호화 키: 파일 키를 암호화하기 위해 사용되는 키. 키링 플러그인에 의해 관리됨.

 

▲ 바이너리 로그 파일의 암호화 방식

7.5.2 바이너리 로그 암호화 키 변경


--- 바이너리 로그 암호화 키 변경(로테이션)
mysql> ALTER INSTANCE ROTATE BINLOG MASTER KEY;
  • 바이너리 로그 암호화 키 변경 과정
    • (1) 증가된 시퀀스 번호와 함께 새로운 바이너리 로그 암호화 키 발급 후 키링 파일에 저장
    • (2) 바이너리 로그 파일과 릴레이 로그 파일 스위치(새로운 로그 파일로 로테이션)
    • (3) 새로 생성되는 바이너리 로그와 릴레이 로그 파일의 암호화를 위해 파일 키를 생성하고, 파일 키는 바이너리 로그 파일 키(마스터 키)로 암호화해서 각 로그 파일에 저장
    • (4) 기존 바이너리 로그와 릴레이 로그 파일의 파일 키를 읽어서 새로운 바이너리 로그 파일 키로 암호화해서 다시 저장 (암호화되지 않은 로그 파일은 무시)
    • (5) 모든 바이너리 로그와 릴레이 로그 파일이 새로운 바이너리 로그 암호화 키로 다시 암호화됐다면 기존 바이너리 로그 암호화 키를 키링 파일에서 제거
  • 4번 과정은 시간이 걸리기 때문에, 키링 파일에서 바이너리 로그 암호화 키는 내부적으로 버전(시퀀스 번호) 관리가 이뤄짐.
    • 예를 들어, 많은 바이너리 로그와 릴레이 로그를 가진 MySQL 서버에서 ALTER INSTANCE ROTATE BINLOG MASTER KEY 명령을 연속으로 2번 실행함.
    • 키링 파일에는 순차적인 시퀀스 번호를 가지는 3개의 바이너리 로그 암호화 키가 존재함.
    • 바이너리 로그와 릴레이 로그 파일들을 최근 순서대로 파일 키를 다시 암호화해서 저장 하는 작업을 수행함.
    • 모든 바이너리 로그와 릴레이 로그 파일의 파일 키가 새로운 바이너리 로그 암호화 키로 암호화되어 저장됨.
    • 더이상 기존 바이너리 로그 암호화 키는 필요치 않으므로 키링 파일에서 제거됨.
--- MySQL 서버의 바이너리 로그 파일이 암호화돼 있는지 확인
mysql> SHOW BINARY LOGS;

7.5.3 mysqlbinlog 도구 활용


  • MySQL 서버에서 트랜잭션의 내용을 추적하거나 백업 복구를 위해 암호화된 바이너리 로그를 평문으로 복호화함.
    • 한 번 바이너리 로그 파일이 암호화되면 바이너리 로그 암호화 키가 없으면 복호화할 수 없음.
    • 바이너리 로그 암호화 키는 MySQL 서버만 가지고 있어서 복호화가 불가능함.
    • mysqlbinlog 도구를 이용해 암호화된 바이너리 로그 파일의 내용을 SQL 문장으로 한번 풀어보면, 암호화된 바이너리 로그 파일을 직접 열어 볼 수는 없다는 에러 메시지를 출력함.
  • 바이너리 로그 파일의 내용을 볼 수 있는 방법은 MySQL 서버를 통해 가져오는 방법이 유일함.
    • 현재 MySQL 서버가 mysql-bin.000011 로그 파일을 가지고 있다는 가정 하에, mysqlbin.000011 로그 파일의 내용을 확인하려면 mysqlbinlog 도구가 MySQL 서버에 접속해서 바이너리 로그를 가져와야 함.
    • 아래 예제에서 파라미터로 주어진 mysqlbin.000011은 MySQL 서버에게 요청할 바이너리 로그 파일의 이름이며, mysqlbinlog 도구가 직접 mysql-bin.000011 파일을 읽는 것은 아님. mysqlbinlog 명령을 실행할 때 --read-from-remote-server 파라미터와 함께 MySQL 서버 접속 정보를 입력해야 함.
linux> mysqlbinlog --read-from-remote-server -uroot -p -vvv mysql-bin.000011
Enter password:
....
BINLOG '
c4YjXx0BAAAASQAAAH8BAACAADFpbnNlcnQgaW50byBlbmMgdmFsdWVzICgyLCdlbmNyeXB0ZWRf
YmluYXJ5IGxvZycpv4dkcA==
c4YjXxMBAAAANwAAALYBAAAAAFsAAAAAAAEABHRlc3QAA2VuYwACAw8CkAEDAQEAAgEtHm4rfQ==
c4YjXx4BAAAAPgAAAPQBAAAAAFsAAAAAAAEAAgAC/wACAAAAFABlbmNyeXB0ZWRfYmluYXJ5IGxv
ZzSCia4=
'/*!*/;
### INSERT INTO `test`.`enc`
### SET ### @1=2 /* INT meta=0 nullable=1 is_null=0 */
### @2='encrypted_binary log' /* VARSTRING(400) meta=400 nullable=1 is_null=0 */
...
추가적으로 할 일
  • MySQL 실습
  • AES 알고리즘 학습

참고자료

1) 개발자 Aiden, "[Java] AES 암호화/복호화 예제( AES-256 )", 2023.02.12, https://aday7.tistory.com/entry/Java-AES-암호화복호화-예제-AES-256

 

이 글은 『Real MySQL 8.0 (1권)』 책을 학습한 내용을 정리한 것입니다.
Comments