MySQL과 SSL/TLS통한 접속

Hyunho Jung
finda 기술 블로그
21 min readAug 21, 2023

Motivation

안녕하세요. 핀다 DBA로 근무하고 있는 정현호 라고 합니다.

이번 글은 SSL/TLS와 MySQL에 접속 시 SSL/TLS에 대한 내용을 이야기해보려고 해요

MySQL도 다른 소프트웨어와 마찬가지로 메이저 및 마이너 버전의 업데이트 마다 새로운 기능 추가나 기존 결함의 수정 그리고 개발 방향성 등에 따라서 명칭이 변경되거나 사라지는 기능이나 옵션 등의 여러가지 변경사항을 포함하게 됩니다.

많이 사용하고 있는 AWS 클라우드의 MySQL 서비스인 RDS for MySQL 도 얼마전 8.0.27 버전이 표준 지원이 종료되어 MySQL 8.0.28버전이 지원되는 가장 낮은 버전이고 Aurora MySQL 서비스에서도 MySQL 8.0.28버전과 호환되는 Aurora MySQL 3.04.0 버전이 릴리즈 되었습니다.

MySQL을 비롯하여 소프트웨어는 새로운 버전이 출시되어 기존의 사용하던 버전을 업데이트(업그레이드) 하는 경우 설치되는 환경과의 문제점이나 호환성(compatibility) 또는 접속을 하는 클라이언트와 서버 간의 호환성 등을 사전에 체크와 테스트가 되어야 업데이트 수행 후에 문제없이 소프트웨어를 사용할 수 있습니다.

그래서 이번 글은 MySQL의 버전을 업데이트 하거나 또는 연관된 드라이버나 개발언어의 버전을 업데이트시에 여러 고려사항 중에서 MySQL과 SSL/TLS에 관한 내용을 이야기해보려고 합니다.

SSL/TLS

MySQL에 대한 내용에 앞에서 SSL/TLS에 대해서 먼저 간략하게 알아보도록 할게요

Image by Freepik

SSL/TLS는 클라이언트와 서버 프로그램이 네트워크로 통신하는 과정에서 도청 및 간섭, 위 변조 방지 등을 위해서 서로 간의 신뢰할 수 있는 전자 서명이 포함된 인증서를 사용하여 암호화 통신 프로토콜을 의미해요

통신 과정에서 전송계층 종단간(End-to-End, 終端間) 보안과 데이터 무결성을 확보하기 위한 기술입니다.

SSL은 Secure Sockets Layer 약자이고 TLS는 Transport Layer Security 약자로 암호화 전송 또는 전송 계층 보안에 의미를 담고 있습니다.

표준적인 명칭은 TLS이지만 통상적으로 SSL 과 TLS을 같이 부르고 있기 때문에 같이 언급을 하였으며 버전에 따라서 SSL에서 TLS로 명칭이 변경되었어요

SSL 프로토콜 규약은 처음 넷스케이프에서 개발하였으며, 이후 국제 인터넷 표준화 기구(IETF)에 의해서 표준 규약으로 정의되었으며, 버전의 역사 정보는 다음과 같아요

SSL/TLS Version History

SSL/TLS는 우리가 알게 모르게 계속해서 사용하고 있는 암호화 통신 프로토콜이에요. 인터넷 브라우저로 웹 사이트에 접속하였을 때 아래와 같이 사이트 주소 앞에 자물쇠 모양의 아이콘을 본적이 있으실 텐데요

Unsplash 의 Richy Great

해당 아이콘이 의미하는 것은 전자 서명이 포함된 인증서를 통해서 서로 간의 신뢰할 수 있음을 의미하며 통신 내용은 암호화하여 전송되고 있음을 나타냅니다.

어떤 프로그램을 설치를 하려고 할 때 설치할 수 있는 운영체제 버전이나 브라우저 종류나 버전이 있고 이것을 호환성(compatibility) 이라고 해요

암호화하여 송수신하는 프로토콜인 SSL/TLS는 클라이언트와 서버 간에도 이와 유사하게 사용할 SSL/TLS 버전을 지정하는 과정을 수행하고 서버와 클라이언트 간의 버전 호환성(compatibility)이 맞아야 통신이 가능하게 돼요

TLS Handshake

우리가 실생활에서 서로 간의 악수를 하는 것과 같이 SSL/TLS 암호화 프로토콜 통신을 위해서 서버와 클라이언트 간의 악수를 하는 과정을 의미해요

Image by Freepik

SSL/TLS 보안 프로토콜을 통해서 안전한 통신을 위해 양측(서버, 클라이언트)에서의 서로에 대한 신뢰된 정보 및 메세지를 교환 및 확인하고 암호화 코드 설정 및 세션 키의 동의를 위해서 메세지를 주고받는 일련의 과정을 거치게 돼요

TLS 핸드셰이크의 원리는 무엇일까요? — CLOUDFLARE

사람이 만나서 인사를 나누듯이 SSL/TLS의 Handshake 과정에서 “client hello” , “server hello’ 의 메세지를 주고받아요

이런 SSL/TLS Handshake 과정에서 클라이언트와 서버 간에 수행하는 것 작업 중에는 각각 사용할 TLS 버전을 지정하는 과정이 있는데요

접속을 요청하는 클라이언트에서도 사용 가능한 SSL/TLS 버전이 정해져 있고, 접속을 받는 서버에서도 사용(지원) 가능한 SSL/TLS 버전이 정해져 있습니다.

그래서 클라이언트와 서버 서로 간에는 사용 가능한 SSL/TLS 버전이 매칭이 되어야, 즉 버전이 호환이 되어야 SSL/TLS Handshake를 정상적으로 할 수 있게 되고 그에 따라서 암호화 송수신 프로토콜을 통한 통신을 할 수 있게 되는 것입니다.

이제 이번 글에서 본격적으로 이야기하려고 하는 부분이 바로 이 SSL/TLS 버전과 관련된 내용입니다.

MySQL 과 SSL/TLS

MySQL 서버도 접속을 받는 서버 소프트웨어(프로그램)으로 SSL/TLS 통신 프로토콜을 사용할 수 있고 위의 내용과 동일하게 버전 별로 지원하는 SSL/TLS 버전에 대한 부분이 정해져 있어요

MySQL에 접속하는 클라이언트(애플리케이션이나 PC 등)에서도 사용 환경에 따라서 지원되는 SSL/TLS 버전이 정해져 있습니다.

그리고 혹시 사용하는 드라이버 또는 클라이언트 툴에 따라서 SSL/TLS로 접속을 기본값으로 사용하는 경우가 많다는 것을 혹시 알고 계셨나요?

지금 사용하는 MySQL 데이터베이스를 확인해 보시면 접속된 많은 세션 중에서 이미 SSL/TLS을 통해서 접속되어 있는 세션이 아마도 있을 거예요!
(조회방법은 다음 챕터 참조)

위에서 SSL/TLS의 버전 호환성과 관련된 내용처럼 MySQL 접속 시 SSL/TLS 암호화 프로토콜 방식으로 접속하려면 TLS 버전 호환성이 맞아야 접속할 수 있습니다.

서두의 Motivation 내용과 같이 최근에 AWS의 MySQL 서비스 RDS for MySQL 8.0.27 버전까지 표준 지원일이 지남에 따라 최소 8.0.28 버전으로 업데이트가 수행이 되었고, Aurora MySQL은 MySQL 커뮤니티 버전 8.0.28과 호환되는 Aurora MySQL 3.04.0 버전이 출시가 되었어요

앞으로는 점점 더 MySQL 8버전(또는 호환된 Aurora)사용시에 8.0.28 버전 이상의 환경에서 사용할 것이라고 예상이 돼요

MySQL 8.0.28 버전에서 여러 변경 내용을 담고 있고, 그 중에서는 TLS 버전1.0, TLS 버전1.1 지원이 제거(remove) 되었다는 내용도 포함되어 있습니다.

Deprecation and Removal Notes
Support for the TLS v1.0 and TLS v1.1 connection protocols is removed as of MySQL 8.0.28.
- Changes in MySQL 8.0.28 (2022–01–18, General Availability)

이 내용은 클라이언트(애플리케이션이나 PC 등)에서 MySQL을 SSL/TLS 방식으로 접속시에 TLS1.0 또는 1.1 버전으로 접속한다면 다음과 같은 에러가 발생되면서 접속이 불가능함을 의미해요

MySQL을 비롯하여 OS나 응용 프로그램은 새로운 버전이 출시하게 되어 기존의 사용하던 버전을 업데이트(업그레이드) 하는 경우 설치되는 환경과의 문제점이나 호환성(compatibility) 또는 접속을 하는 클라이언트와 서버 간의 호환성 등을 사전에 체크와 테스트가 되어야, 업데이트 후에 문제없이 프로그램을 사용할 수 있게 됩니다.

MySQL 서버는 대부분이 네트워크(TCP/IP)를 통한 접속을 하고 있고 그래서 버전 업데이트시 클라이언트와 서버 간의 접속에 대한 호환성이 매우 중요한 부분이라고 할 수 있어요

이전 버전의 MySQL 에서 MySQL 8버전으로 업데이트하거나 기존의 MySQL 8 버전에서 마이너 버전 업데이트 할 경우 향후에 결국은 8.0.28 버전 이상으로 업데이트를 하게 될 것입니다.

그에 따라 MySQL 업데이트시 고려해야 할 내용과 사전 체크하였던 여러 항목 중에서 사용하는 클라이언트의 SSL/TLS 버전 지원 범위에 대한 부분도 추가로 체크를 해야 하는 상황이라고 설명할 수 있을 것 같아요

사용중인 MySQL 서버에서 지원하는 SSL/TLS 버전에 대한 정보는 다음과 같이 확인할 수 있어요

mysql> select @@version;
+------------+
| @@version |
+------------+
| 5.7.37-log |
+------------+

mysql> show global variables like 'tls_version';
+---------------+-----------------------+
| Variable_name | Value |
+---------------+-----------------------+
| tls_version | TLSv1,TLSv1.1,TLSv1.2 |
+---------------+-----------------------+

위의 환경은 MySQL 5.7.37버전이고 지원가능한 버전은 TLSv1, TLSv1.1,TLS v1.2까지 인 것을 확인할 수 있어요

예전 개발 언어 버전 및 드라이버 버전을 사용한 샘플 Java 와 Python 프로그램을 통해서 테스트를 해보도록 할게요

2개 프로그램 모두 MySQL 서버에 접속하여 접속한 MySQL 서버 버전, 접속한 클라이언트의 SSL/TLS 버전 정보를 표시하는 간단한 샘플 프로그램입니다.

### Java
$ java TestConnection_mysqldriver
version: 5.7.37-log, SSL/TLS Version: TLSv1.1, SSL cipher: ECDHE-RSA-AES256-SHA

### Python
$ python3 py_mysql_connect_test.py
version: 5.7.37-log ,SSL Version: TLSv1 ,SSL Cipher: ECDHE-RSA-AES256-SHA

접속에 사용된 SSL/TLS 버전은 TLSv1.1 TLSv1 으로 확인되고 있어요

이제 상위 버전인 MySQL 8.0.28로 접속을 시도하면 다음과 같은 에러가 발생되게 됩니다.

### Java
$ java TestConnection_mysqldriver
Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 513 milliseconds ago.
The last packet sent successfully to the server was 506 milliseconds ago.

< ... 중략 ... >
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: protocol_version
at sun.security.ssl.Alert.createSSLException(Alert.java:131)
at sun.security.ssl.Alert.createSSLException(Alert.java:117)
at sun.security.ssl.TransportContext.fatal(TransportContext.java:311)
at sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:293)
at sun.security.ssl.TransportContext.dispatch(TransportContext.java:185)
at sun.security.ssl.SSLTransport.decode(SSLTransport.java:152)
at sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1397)
at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1305)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:440)
at com.mysql.jdbc.ExportControlled.transformSocketToSSLSocket(ExportControlled.java:188)



### Python
$ python3 py_mysql_connect_test.py
Traceback (most recent call last):
File "..../site-packages/mysql/connector/network.py",
line 421, in switch_to_ssl
self.sock.do_handshake()
File "..../ssl.py", line 767, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: TLSV1_ALERT_PROTOCOL_VERSION]
tlsv1 alert protocol version (_ssl.c:598)

During handling of the above exception, another exception occurred:

위의 에러 메세지를 통해서 확인할 수 있는 내용은 이전장의 SSL/TLS Handshake 를 설명한 내용과 같이 SSL Handshake 과정에서의 문제가 발생되었음을 알 수 있습니다.

발생된 2개의 메세지 TLSV1_ALERT_PROTOCOL_VERSION 또는 protocol_version 를 통해서 SSL Handshake의 과정에서의 TLS Version에 의해서 문제가 발생되었다는 점도 에러 메세지를 통해 확인할 수 있는 부분입니다.

이와 같이 SSL/TLS 암호화 프로토콜은 서버와 클라이언트간의 통신을 위한 Handshake 과정에서 서로 간의 사용할 TLS버전을 정하게 되는데 서로 공통된 TLS버전이 존재하지 않는다면 Handshake과정이 실패하게 되어 접속이 불가능하게 돼요

MySQL 8.0.28 버전 릴리즈 노트의 내용과 같이 TLSv1, TLSv1.1 이 Remove 되어서 지원되는 TLS 버전이 1.2 와 1.3 인 것을 확인할 수 있습니다.

mysql> select @@version;
+-----------+
| @@version |
+-----------+
| 8.0.28 |
+-----------+

mysql> SHOW GLOBAL VARIABLES LIKE 'tls_version';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tls_version | TLSv1.2,TLSv1.3 |
+---------------+-----------------+

다수의 서비스를 하는 운영 환경에서는 각각의 개발언어나 드라이버와 그리고 접속하는 대상(서버)에서 사용가능한(지원되는) TLS 버전이 각각 상황이 다를 것입니다.

그래서 개발언어(또는 접속 드라이버) 또는 접속하는 대상(여기서는 데이터베이스)의 버전 업데이트는 위와 같이 SSL/TLS 버전의 지원의 불일치(mismatch)을 상황을 만날 수 있 도 있게 되는 것이지요

그렇다면 서버 소프트웨어의 변경(업데이트 등)에 의해서만 이러한 문제가 발생될 수 있을까요? 그렇지는 않아요!

SSL Handshake 과정에서 서버와 클라이언트 간에 사용할 TLS 버전을 설정한다는 의미는 클라이언트의 변화에 의해서도 SSL/TLS 버전이 불일치할 수도 있음을 의미해요

예를 들어 Java 8 버전의 Update 291 버전부터는 TSLv1.0 과 TLSv1.1 이 더이상 지원을 하지 않은 비활성화 상태로 변경이 되었어요

그래서 TLSv1.2를 지원하지 않은 예전의 MySQL 5.7 버전이거나 그 이전 버전을 사용하고 있을 경우에도 동일 하게 SSL Handshake 에러가 발생하게 됩니다.

The last packet sent successfully to the server was 0 milliseconds ago. 
The driver has not received any packets from the server.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
<... 중략 ...>
Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled
or cipher suites are inappropriate)
at sun.security.ssl.HandshakeContext.<init>(HandshakeContext.java:171)
at sun.security.ssl.ClientHandshakeContext.<init>(ClientHandshakeContext.java:98)
at sun.security.ssl.TransportContext.kickstart(TransportContext.java:220)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:428)
at com.mysql.cj.protocol.ExportControlled.performTlsHandshake(ExportControlled.java:336)
at com.mysql.cj.protocol.StandardSocketFactory.performTlsHandshake(StandardSocketFactory.java:188)
at com.mysql.cj.protocol.a.NativeSocketConnection.performTlsHandshake(NativeSocketConnection.java:99)
at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:325)

이와 같이 SSL/TLS 를 사용하는 환경에서는 서버 와 클라이언트(드라이버 또는 개발언어) 모두 버전을 변경할 경우 SSL/TLS 지원 버전에 대한 정보를 미리 확인이 필요해요

핀다에서도 작년에는 일부에서 예전 개발 언어 버전이 사용되어 다음과 같이 TLS v1.1 로 접속된 내용을 확인할 수 있었는데요

작년에 핀다의 빠른 압축 성장과 함께 많은 개발자 분들이 합류해 주셔서 지금은 적절한 최신의 개발 언어 버전 과 개발 언어로 변화하였고 그에 따라서 이제 핀다에서의 기술부채는 찾아볼 수 없게 되었어요!

기술부채 — 파이콘 2017

그래서 지금 TLS v1.2에서 TLS v1.3까지 적절한 TLS 버전을 만족하는 환경으로 사용 중입니다.

SSL/TLS 조회

MySQL 에서 접속된 애플리케이션 스레드의 접속 방식 및 TLS 버전에 대한 확인은 sys.session_ssl_status 와 performance_schema.threads 을 조인(join) 을 해서 조회하면 확인할 수 있습니다.

mysql> select b.processlist_user,
b.processlist_id,
b.connection_type,
a.ssl_version,
a.ssl_cipher
from sys.session_ssl_status a join performance_schema.threads b
on a.thread_id= b.thread_id
where ssl_version is not null
order by ssl_version desc;

다만 다른 스레드의 SSL/TLS 정보를 확인하기 위해서는 performance_schema가 활성화되어 있어야 확인 가능 합니다.

접속한 자기 스레드에 대한 정보는 다음과 같이 확인할 수 있습니다.

mysql> show session status like 'ssl_version';

특이점으로는 글을 작성하는 시점 기준으로 AWS Aurora MySQL 2 버전대에서는 performance_schema.status_by_thread 에서 SSL/TLS에 대한 정보가 출력되지 않는 다는 점이에요

2 버전에서 모두 나타나는 현상이고 AWS 클라우드 Support Case를 통해 확인한 결과, 간략하게 정리하면 현재로 써는 Aurora MySQL 2 버전의 제한사항이라는 내용으로 확인받았어요
(향후에는 조회가 가능할 수도 있어요!)

이처럼 SSL/TLS를 사용할 경우에는 버전 업데이트 수행하기 전에 서버와 클라이언트간 TLS 지원 버전도 확인을 해야 합니다.

이런 경우 문제를 해결하기 위해서 할 수 있는 조치 사항 또는 사전에 문제가 발생되지 않기 위해서 할 수 있는 준비 사항은 크게 다음과 같은 부분을 생각해 볼 수 있을 것 같아요

  • 클라이언트와 MySQL 서버 둘 다 적절한 버전을 유지
    개발언어나 서버 프로그램에서도 LTS(Long Term Support) 라고 표현되는 장기 지원 버전이 있는 경우, LTS 버전대를 사용한다면 보통의 경우 문제가 될 확률이 상대적으로 적습니다.
    LTS 버전은 아니지만 EOS(End of support)가 끝나지 않은 안정 버전(또는 최신버전) 사용 중이라면 마이너 버전의 업데이트의 변경에서는 보통의 경우 TLS 버전은 맞출 수 있게 됩니다.
  • SSL/TLS 접속을 비활성화
    각 개발 언어의 접속 드라이버 설정 속성에서는 SSL/TLS 접속에 대해서 사용 또는 미사용을 선택할 수 있는 옵션이 보통은 제공 됩니다.
    예를 들어 Java 에서는 useSSL=false 를 통해서 SSL/TLS 접속이 아닌 Non-SSL 방식으로 접속할 수 있습니다.

당장 문제가 발생된 긴급한 경우라면 SSL/TLS 미사용 설정으로 변경하여 MySQL로 접속하는 것도 한가지 방법이 될 수도 있을 것 같습니다.

다만, 운영 정책에서 SSL/TLS의 사용이 권장 또는 필수인 경우가 있을 수 있고 계속되는 보안적인 위험도가 늘어나는 요즘의 IT환경에서 SSL/TLS 보안 통신 프로토콜을 사용하는 것이 당연히 권장된다고 생각하고 있어요

앞으로는 SSL/TLS 사용이 더욱 보편화되고 일반적인 환경이 될 수 있는 예상되는 환경인 만큼 MySQL에서의 SSL/TLS 사용에 대해서도 한번 쯤 생각해볼 수 있는 시간이 되었으면 되었으면 합니다!

끝으로

이번 글에서는 SSL/TLS과 MySQL에 대한 내용에 대해서 알아보았고 주요 내용을 정리해볼게요

  • SSL/TLS 방식으로 접속 시 공통되는 SSL/TLS 버전이 존재해야 함
  • 서버 와 클라이언트 모두 지원 SSL/TLS 버전에 대해서 확인이 필요함
  • MySQL에 접속 시 많은 경우 이미 SSL/TLS를 통해 접속하여 사용중임
  • MySQL 8.0.28 버전 부터 TLSv1.0, TLSv1.1 이 제거(Remove) 되었음

긴 글을 읽어주셔서 감사해요!

아직 많이 더운 여름 날씨인데요 건강하시고 행복한 여름 보내시길 바랄게요

SSL/TLS 접속과 관련되어 “AWS RDS 인증서 만료에 따른 인증서 업데이트” 글로 내용이 이어집니다

다음에 또 만나요👋 감사합니다.

--

--

Hyunho Jung
finda 기술 블로그

Database Administrator at finda, Tech Stack : MySQL, AWS Aurora , Oracle, MongoDB, Linux