1. 개요

MySQL 서버와 클라이언트 간의 암호화되지 않은 연결은 네트워크를 통해 전송 중인 데이터를 노출할 수 있습니다. 프로덕션 준비 애플리케이션의 경우 모든 통신을 TLS(전송 계층 Security) 프로토콜을 통한 Security 연결로 이동해야 합니다. 

이 사용방법(예제)에서는 MySQL 서버에서 Security 연결을 활성화하는 방법을 배웁니다. 또한 이 Security 연결을 사용하도록 Spring Boot 애플리케이션을 구성합니다.

2. MySQL에서 TLS를 사용하는 이유는 무엇입니까?

먼저 TLS의 몇 가지 기본 사항을 이해하겠습니다.

TLS 프로토콜 은 암호화 알고리즘을 사용하여 네트워크를 통해 수신된 데이터를 신뢰할 수 있고 변조되거나 검사되지 않도록 합니다. 데이터 변경, 손실 또는 재생 공격을 감지하는 메커니즘이 있습니다. TLS는 또한 X.509 표준을 사용하여 신원 확인을 제공하는 알고리즘을 통합합니다.

암호화된 연결은 Security 계층을 추가하고 네트워크 트래픽을 통해 데이터를 읽을 수 없게 만듭니다.

MySQL 서버와 클라이언트 간의 Security 연결을 구성하면 인증, 데이터 무결성 및 신뢰성이 향상됩니다. 또한 MySQL 서버는 클라이언트의 ID에 대한 추가 검사를 수행할 수 있습니다.

그러나 이러한 Security 연결에는 암호화로 인한 성능 저하가 따릅니다. 성능 비용의 심각성은 쿼리 크기, 데이터 로드, 서버 하드웨어, 네트워크 대역폭 및 기타 요소와 같은 다양한 요소에 따라 다릅니다.

3. MySQL 서버에서 TLS 연결 구성

MySQL 서버는 연결 단위로 암호화를 수행하며, 이는 지정된 사용자에 대해 필수 또는 선택 사항으로 설정할 수 있습니다. MySQL은 설치된 OpenSSL 라이브러리를 사용하여 런타임 시 SSL 암호화 관련 작업을 지원합니다.

JDBC Driver Connector/J 를 ​​사용 하여 초기 핸드셰이크 후 클라이언트와 서버 간의 데이터를 암호화할 수 있습니다. 

MySQL 서버 v8.0.28 이상은 TLS v1.2 및 TLS v1.3만 지원합니다. 더 이상 이전 버전의 TLS(v1 및 v1.1)를 지원하지 않습니다.

서버 인증은 신뢰할 수 있는 루트 인증 기관에서 서명한 인증서 또는 자체 서명된 인증서를 사용하여 활성화할 수 있습니다 . 또한 프로덕션 환경에서도 MySQL용 루트 CA 파일을 빌드 하는 것이 일반적 입니다.

또한 서버는 클라이언트의 SSL 인증서를 인증 및 확인하고 클라이언트의 ID에 대한 추가 검사를 수행할 수 있습니다.

3.1. TLS 인증서로 MySQL 서버 구성

require_secure_transport 속성 과 기본 생성 인증서 를 사용하여 MySQL 서버에서 Security 전송을 활성화합니다  .

docker-compose.yml 에 설정을 구현하여 MySQL 서버를 빠르게 부트스트랩해 보겠습니다 .

version: '3.8'

services:
  mysql-service:
    image: "mysql/mysql-server:8.0.30"
    container_name: mysql-db
    command: [ "mysqld",
      "--require_secure_transport=ON",
      "--default_authentication_plugin=mysql_native_password",
      "--general_log=ON" ]
    ports:
      - "3306:3306"
    volumes:
      - type: bind
        source: ./data
        target: /var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_HOST: "%"
      MYSQL_ROOT_PASSWORD: "Password2022"
      MYSQL_DATABASE: test_db

위의 MySQL 서버는 /var/lib/mysql 경로에 있는 기본 인증서를 사용합니다 .

또는 docker-compose.yml 에 몇 가지 mysqld 구성 을 포함하여 기본 인증서를 재정의할 수 있습니다 .

command: [ "mysqld",
  "--require_secure_transport=ON",
  "--ssl-ca=/etc/certs/ca.pem",
  "--ssl-cert=/etc/certs/server-cert.pem",
  "--ssl-key=/etc/certs/server-key.pem",
  ....]

이제 docker-compose 명령 을 사용하여 mysql-service 를 시작하겠습니다.

$ docker-compose -p mysql-server up

3.2. X509 로 사용자 생성

선택적으로 X.509 표준을 사용하여 클라이언트 식별로 MySQL 서버를 구성할 수 있습니다. X509 의 경우 유효한 클라이언트 인증서가 필요합니다. 이것은 양방향 상호 TLS 또는 mTLS 를 활성화 합니다.

X509 로 사용자를 만들고  test_db  데이터베이스  에 대한 권한을  부여해 보겠습니다.

mysql> CREATE USER 'test_user'@'%' IDENTIFIED BY 'Password2022' require X509;
mysql> GRANT ALL PRIVILEGES ON test_db.* TO 'test_user'@'%';

사용자 인증서 식별 없이 TLS 연결을 설정할 수 있습니다.

mysql> CREATE USER 'test_user'@'%' IDENTIFIED BY 'Password2022' require SSL;

SSL이 사용되는 경우 클라이언트는 신뢰 저장소를 제공해야 합니다 .

4. 스프링 부트 애플리케이션에서 TLS 구성

Spring Boot 애플리케이션은 몇 가지 속성으로 JDBC URL을 설정하여 JDBC 연결을 통해 TLS를 구성할 수 있습니다.

MySQL 과 함께 TLS를 사용하도록 Spring Boot 애플리케이션을 구성하는 다양한 방법이 있습니다 .

그 전에 신뢰 저장소와 클라이언트 인증서를 JKS 형식으로 변환해야 합니다.

4.1. PEM 파일을 JKS 형식으로 변환

MySQL 서버 생성 ca.pemclient-cert.pem 파일을 JKS 형식으로 변환해 보겠습니다.

keytool -importcert -alias MySQLCACert.jks -file ./data/ca.pem \
    -keystore ./certs/truststore.jks -storepass mypassword
openssl pkcs12 -export -in ./data/client-cert.pem -inkey ./data/client-key.pem \
    -out ./certs/certificate.p12 -name "certificate"
keytool -importkeystore -srckeystore ./certs/certificate.p12 -srcstoretype pkcs12 -destkeystore ./certs/client-cert.jks

Java 9부터 기본 키 저장소 형식은 PKCS12 입니다 .

4.2. application.yml 을 사용하여 구성

TLS는 sslMode 를 PREFERRED , REQUIRED , VERIFY_CA 또는 VERIFY_IDENTITY 로 설정  하여 활성화할 수 있습니다 .

PREFERRED 모드 는 서버가 지원하는 경우 Security 연결을 사용하거나 그렇지 않으면 암호화되지 않은 연결로 폴백합니다.

REQUIRED 모드 를 사용 하면 클라이언트는 암호화된 연결만 사용할 수 있습니다. REQUIRED마찬가지로 VERIFY_CA 모드는 Security 연결을 사용하지만 구성된 인증 기관(CA) 인증서에 대해 서버 인증서의 유효성을 추가로 확인합니다.

VERIFY_IDENTITY 모드는 인증서 유효성 검사와 함께 호스트 이름을 한 번 더 확인합니다.

또한 trustCertificateKeyStoreUrl , trustCertificateKeyStorePassword , clientCertificateKeyStoreUrlclientCertificateKeyStorePassword 와 같은 몇 가지 Connector/J 속성을 JDBC URL에 추가해야 합니다 .

sslMode 가 VERIFY_CA 로 설정된 application.yml 에서 JDBC URL을 구성해 보겠습니다 .

spring:
  profiles: "dev2"
  datasource:
    url: >-
         jdbc:mysql://localhost:3306/test_db?
         sslMode=VERIFY_CA&
         trustCertificateKeyStoreUrl=file:/<project-path>/mysql-server/certs/truststore.jks&
         trustCertificateKeyStorePassword=mypassword&
         clientCertificateKeyStoreUrl=file:/<project-path>/mysql-server/certs/client-cert.jks&
         clientCertificateKeyStorePassword=mypassword
    username: test_user
    password: Password2022

VERIFY_CA 에 해당하는 더 이상 사용되지 않는 속성 은 useSSL=trueverifyServerCertificate=true 의 조합입니다 .

신뢰 인증서 파일이 제공되지 않으면 해당 오류가 발생합니다.

Caused by: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors
	at java.base/sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:157) ~[na:na]
	at java.base/sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:83) ~[na:na]
	at java.base/java.security.cert.CertPathValidator.validate(CertPathValidator.java:309) ~[na:na]
	at com.mysql.cj.protocol.ExportControlled$X509TrustManagerWrapper.checkServerTrusted(ExportControlled.java:402) ~[mysql-connector-java-8.0.29.jar:8.0.29]

클라이언트 인증서가 누락된 경우 다른 오류가 발생합니다.

Caused by: java.sql.SQLException: Access denied for user 'test_user'@'172.20.0.1'

4.3. 환경 변수를 사용하여 TLS 구성

또는 위의 구성을 환경 변수로 설정하고 SSL 관련 구성을 JVM 매개변수로 포함할 수 있습니다.

TLS 및 Spring 관련 구성을 환경 변수로 추가해 보겠습니다.

export TRUSTSTORE=./mysql-server/certs/truststore.jks
export TRUSTSTORE_PASSWORD=mypassword
export KEYSTORE=./mysql-server/certs/client-cert.jks
export KEYSTORE_PASSWORD=mypassword
export SPRING_DATASOURCE_URL=jdbc:mysql://localhost:3306/test_db?sslMode=VERIFY_CA
export SPRING_DATASOURCE_USERNAME=test_user
export SPRING_DATASOURCE_PASSWORD=Password2022

그런 다음 위의 SSL 구성으로 애플리케이션을 실행해 보겠습니다.

$java -Djavax.net.ssl.keyStore=$KEYSTORE \
 -Djavax.net.ssl.keyStorePassword=$KEYSTORE_PASSWORD \
 -Djavax.net.ssl.trustStore=$TRUSTSTORE \
 -Djavax.net.ssl.trustStorePassword=$TRUSTSTORE_PASSWORD \
 -jar ./target/spring-boot-mysql-0.1.0.jar

5. TLS 연결 확인

이제 위의 방법 중 하나를 사용하여 애플리케이션을 실행하고 TLS 연결을 확인하겠습니다.

TLS 연결은 MySQL 서버 일반 로그를 사용하거나 프로세스  및  시스템 관리 테이블 을 쿼리하여 확인할 수 있습니다 .

기본 경로 /var/lib/mysql/ 에 있는 로그 파일을 사용하여 연결을 확인하겠습니다 .

$ cat /var/lib/mysql/7f44397082d7.log
2022-09-17T13:58:25.887830Z        19 Connect   test_user@172.22.0.1 on test_db using SSL/TLS

또는 test_user 가 사용하는 연결을 확인하겠습니다 .

mysql> SELECT process.thd_id,user,db,ssl_version,ssl_cipher FROM sys.processlist process, sys.session_ssl_status session 
where process.user='test_user@172.20.0.1'and process.thd_id=session.thread_id;+--------+----------------------+---------+-------------+------------------------+
| thd_id | user                 | db      | ssl_version | ssl_cipher             |
+--------+----------------------+---------+-------------+------------------------+
|    167 | test_user@172.20.0.1 | test_db | TLSv1.3     | TLS_AES_256_GCM_SHA384 |
|    168 | test_user@172.20.0.1 | test_db | TLSv1.3     | TLS_AES_256_GCM_SHA384 |
|    169 | test_user@172.20.0.1 | test_db | TLSv1.3     | TLS_AES_256_GCM_SHA384 |

6. 결론

이 기사에서는 MySQL에 대한 TLS 연결이 네트워크를 통해 데이터를 안전하게 보호하는 방법을 배웠습니다. 또한 Spring Boot 애플리케이션에서 MySQL 서버에서 TLS 연결을 구성하는 방법을 살펴보았습니다.

항상 그렇듯이 예제 코드는 GitHub 에서 찾을 수 있습니다 .

Security footer banner