rastalion.dev
MYSQL

MySQL의 TLS/SSL: 내부 네트워크에서 정말 필요할까?

teinam 2024-07-29updated 09-20 7 MIN

MySQL의 암호화 통신

데이터베이스로 가는 트래픽을 암호화하라는 권고는 어디서나 볼 수 있습니다. 그런데 AWS VPC나 사내 내부망처럼 외부 접근이 통제된 구간이라면 어떨까요? 애플리케이션 서버와 MySQL 사이에도 TLS를 걸어야 할까요?

이 글은 그 질문을 세 축으로 나눠 봅니다. 법령이 실제로 무엇을 요구하는지, 암호화가 막는 위협이 그 구간에 있는지, 켜기로 정했을 때 무엇을 설정해야 하는지입니다.

NOTE — 2026-09 현재 지원되는 MySQL 라인은 8.4 LTS와 9.7 LTS입니다. 8.0은 2026-04-21부로 Oracle Sustaining Support로 넘어갔습니다. 이 글의 설정값은 8.4 기준입니다.

1. TLS/SSL의 기본 개념

TLS는 네트워크 구간에 세 가지를 제공합니다.

  • 기밀성 — 데이터를 암호화해 제3자가 읽지 못하게 합니다.
  • 무결성 — 전송 중 변경·유실·재전송을 탐지합니다.
  • 인증 — X.509 인증서로 통신 상대의 신원을 확인합니다.

이름부터 짚고 갑니다. MySQL 공식 문서는 TLS를 SSL이라고 부르는 관행을 인정하면서도, SSL의 암호화가 약하기 때문에 MySQL이 실제로 SSL 프로토콜을 쓰지는 않는다고 명시합니다. 설정 변수와 상태 변수에 ssl_ 접두어가 남아 있는 것은 역사적 이유입니다.

지금 이 연결이 암호화되고 있는가

-- 값이 비어 있으면 이 연결은 암호화되지 않았습니다
SHOW STATUS LIKE 'ssl_cipher';

-- 어떤 프로토콜로 붙었는지까지 확인합니다
SHOW SESSION STATUS LIKE 'Ssl_version';

두 값 모두 지금 세션에 대한 값입니다. SHOW STATUS는 범위를 생략하면 SESSION입니다.

8.4가 받아들이는 프로토콜

MySQL 8.4는 TLSv1.2와 TLSv1.3만 지원합니다. TLSv1과 TLSv1.1은 8.0.26에서 폐기 예고됐고 8.0.28에서 제거됐습니다. 문서는 두 프로토콜이 쓰는 알고리즘이 약하고 낡았다는 이유를 적습니다.

tls_version의 기본값은 서버가 링크한 SSL 라이브러리와 해당 릴리스가 지원하는 프로토콜 전체이고, 8.4에서는 보통 TLSv1.2,TLSv1.3입니다. TLSv1.3은 OpenSSL 1.1.1 이상으로 컴파일된 서버에서만 쓸 수 있습니다. 서버는 기동할 때 OpenSSL 버전을 확인해 1.1.1보다 낮으면 기본값에서 TLSv1.3을 빼고 TLSv1.2만 남깁니다.

켜지 않았는데 켜져 있을 수 있습니다

서버는 인증서와 키 파일을 자동으로 찾습니다. 데이터 디렉터리에 ca.pem·server-cert.pem·server-key.pem이라는 이름의 유효한 파일이 있으면 암호화 연결 지원을 스스로 켜고 에러 로그에 기록을 남깁니다. OpenSSL로 컴파일한 서버는 이 파일이 없을 때 기동 시점에 자동 생성할 수도 있습니다. 클라이언트 기본값도 같은 방향입니다. MySQL 클라이언트 프로그램은 서버가 지원하면 암호화 연결을 시도하고, 맺지 못하면 평문으로 내려갑니다.

그래서 “우리는 TLS를 안 씁니다”가 사실이 아닐 수 있고, “TLS를 켰습니다”가 모든 연결에 대해 참이 아닐 수도 있습니다. 논의의 출발점은 위 상태 변수로 실제 연결을 확인하는 것입니다.

2. 내부 네트워크에서의 TLS/SSL: 장단점 분석

장점

  1. 기밀성 보장 — 같은 세그먼트에 올라온 스니핑과 중간자 공격을 막습니다.
  2. 심층 방어 — 네트워크 통제가 무너졌을 때 남는 계층입니다. 저장 데이터 암호화는 이 구간을 보호하지 않습니다. MySQL 문서는 데이터가 네트워크에 올라간 시점에는 평문 형태라고 명시합니다.
  3. 규정 준수 — 적용받는 기준이 전송 구간 암호화를 요구하는 경우입니다(3절).

단점

  1. 성능 오버헤드 — 암복호에 CPU를 쓰고 지연이 늘어납니다. 비용이 몰리는 지점은 핸드셰이크입니다(5절).
  2. 인증서 수명 관리 — 발급·배포·갱신이 운영 업무로 남고, 만료는 장애로 옵니다.
  3. 관찰 비용 — 패킷 캡처로 쿼리를 들여다보던 방식이 막힙니다.

3. 법령은 무엇을 요구하는가

국내에서 전송 구간 암호화 의무의 근거는 「개인정보의 안전성 확보조치 기준」 제7조입니다. 현행 고시는 개인정보보호위원회고시 제2026-9호이고 2026-07-01 시행입니다. 상위 근거는 시행령 제30조제1항제4호 다목입니다.

제7조에서 전송 암호화를 요구하는 항은 둘이고, 적용 구간이 서로 다릅니다.

대상 구간
제1항 비밀번호·생체인식정보 등 인증정보 정보통신망 송수신 시, 구간 제한 없음
제4항 개인정보 정보통신망을 통해 인터넷망 구간으로 송·수신할 때

이 차이가 질문의 핵심입니다. 제4항은 인터넷망 구간을 전제하므로, 문언대로 읽으면 내부망 안의 애플리케이션 서버와 DB 사이 구간은 제4항의 범위 밖입니다. 고시 제2조제12호는 내부망을 인터넷망 차단이나 접근 통제시스템으로 인터넷 구간에서의 접근이 통제·차단되는 구간으로 정의합니다. DB 연결에 TLS를 쓰라고 직접 요구하는 조문은 없습니다.

그런데 제1항은 구간을 제한하지 않습니다. DB 접속은 그 자체로 인증정보를 실어 보냅니다. 내부망이라도 인증정보가 같은 연결을 타고 흐른다면 제1항이 그 구간에 걸립니다. 조문만 근거로 “내부망이니 켜지 않아도 된다”고 정리하기 어려운 이유가 여기 있습니다.

조문 좌표와 이용자·비이용자 트랙 구분은 데이터 암호화 문서에 정리해 두었습니다.

조문과 별개로, 내부망이어도 TLS를 켜는 쪽으로 기울게 하는 조건이 있습니다.

  1. 멀티 테넌트 환경 — 같은 VPC나 클러스터에서 여러 고객의 데이터를 처리하는 경우
  2. 민감도가 높은 데이터 — 결제 정보나 의료 기록처럼 유출 시 피해가 큰 데이터를 다루는 경우
  3. 복잡한 네트워크 토폴로지 — 여러 세그먼트·리전·계정을 지나 어느 구간이 인터넷망인지 단정하기 어려운 경우
  4. 일관된 보안 정책 — 개발·테스트·운영에서 같은 설정을 유지하려는 경우. 운영에만 켜 두면 인증서 문제를 배포 시점에 발견합니다.

4. TLS/SSL 없이 보안을 강화하는 방법

내부망에서 TLS를 켜지 않기로 정한다면, 그 판단을 받쳐 줄 통제가 필요합니다.

  1. 네트워크 분리 — VLAN, 서브넷, NACL로 데이터베이스 트래픽을 격리합니다.
  2. 강력한 인증 — 다단계 인증(MFA)과 IAM 역할을 사용합니다.
  3. 암호화된 스토리지 — 저장 데이터(data at rest) 암호화를 사용합니다.
  4. 최소 권한 원칙 — 필요한 최소한의 권한만 부여합니다.
  5. 네트워크 모니터링 — 이상 징후를 탐지하기 위한 실시간 모니터링을 구현합니다.

AWS 환경에서는 다음과 같이 구성할 수 있습니다.

VPC:
  SecurityGroups:
    - InboundRule:
        Protocol: TCP
        Port: 3306
        Source: 10.0.0.0/16  # 내부 네트워크만 허용
  NetworkACLs:
    - Rule:
        Action: ALLOW
        Protocol: TCP
        Port: 3306
        Source: 10.0.0.0/16  # 내부 네트워크만 허용

RDS:
  EncryptionAtRest: true
  IAMDatabaseAuthentication: enabled

CloudWatch:
  Alarms:
    - Metric: DatabaseConnections
      Threshold: 100
      Period: 5 minutes

이 조치들은 네트워크에 올라간 평문 자체를 바꾸지 않습니다. TLS를 대체하는 것이 아니라, 접근 경로를 좁혀 위험을 낮추는 쪽입니다.

5. 켤 때 확인할 설정과 성능

강제 지점은 셋입니다

require_secure_transport는 Boolean이고 기본값은 OFF입니다. 켜면 서버는 TLS로 암호화된 TCP/IP 연결, Unix 소켓 파일 연결, Windows 공유 메모리 연결만 허용하고 나머지는 ER_SECURE_TRANSPORT_REQUIRED로 거절합니다. 이 변수만으로는 모든 연결이 TLS라고 말할 수 없습니다. 소켓 파일 접속은 통과합니다.

계정별 요구가 이 변수보다 우선합니다. 문서는 REQUIRE SSL로 만든 계정이라면 require_secure_transport를 켜도 Unix 소켓 파일로는 접속할 수 없다고 적습니다.

[mysqld]
require_secure_transport=ON
tls_version=TLSv1.2,TLSv1.3

클라이언트 쪽은 --ssl-mode로 갈립니다.

동작
DISABLED 평문으로 접속하고 다른 --ssl-xxx 옵션을 무시합니다
PREFERRED 기본값. 암호화를 시도하고 실패하면 평문으로 내려갑니다
REQUIRED 암호화 연결을 맺지 못하면 실패합니다
VERIFY_CA 암호화를 요구하고 서버 CA 인증서를 검증합니다
VERIFY_IDENTITY 여기에 인증서의 호스트 이름 일치까지 확인합니다

문서는 기본값 PREFERRED가 서버 신원을 확인하지 않는다는 점을 짚고, 정교한 중간자 공격을 막으려면 VERIFY_CAVERIFY_IDENTITY를 쓰라고 권합니다. 두 값이 기본이 아닌 이유는 모든 클라이언트에 CA 인증서를 안정적으로 배포하지 않으면 가용성 문제가 생기기 때문입니다. 서버가 자동 생성한 자기서명 인증서에는 Common Name에 서버 이름이 없어 VERIFY_IDENTITY가 동작하지 않으므로, 1절의 자동 발견으로 켜진 TLS를 그대로 두면 신원 검증까지는 가지 못합니다.

MySQL 접속이 통과하는 TLS 강제 지점 A workflow diagram generated by Archify. 01 / 클라이언트 02 / 서버·계정 강제 03 / 거절과 평문 접속 시도 · --ssl-mode · 클라이언트 접속 시도 --ssl-mode 암호화 연결 · VERIFY_CA 이상 · 클라이언트 암호화 연결 VERIFY_CA 이상 전송 방식 · require_secure_transport · 서버·계정 강제 전송 방식 require_secure_transport 계정 요구 · REQUIRE SSL · 서버·계정 강제 계정 요구 REQUIRE SSL 핸드셰이크 · tls_version · 서버·계정 강제 핸드셰이크 tls_version 전송 거절 · 소켓은 통과한다 · 거절과 평문 전송 거절 소켓은 통과한다 계정 거절 · 소켓도 막힌다 · 거절과 평문 계정 거절 소켓도 막힌다 평문 연결 · PREFERRED 로 내려감 · 거절과 평문 평문 연결 PREFERRED 로 내려감 암호화 없음 소켓 접속 핸드셰이크 실패 REQUIRED 이상 범례 클라이언트 핸드셰이크 서버·계정 강제 약한 결과
세 강제 지점과 각 지점에서 갈라지는 약한 결과

세 지점을 다 지나야 암호화 연결입니다. 하나만 켜 두면 아래 칸의 결과 가운데 하나로 떨어지고, 그중 평문 연결은 오류 없이 조용히 맺힙니다.

암호군은 프로토콜별로 변수가 다릅니다. TLSv1.2는 서버 ssl_cipher와 클라이언트 --ssl-cipher, TLSv1.3은 서버 tls_ciphersuites와 클라이언트 --tls-ciphersuites입니다. tls_ciphersuites를 설정하지 않으면 기본값 NULL로 기본 집합을 허용하고, 빈 문자열로 두면 활성 암호군이 없어 암호화 연결을 맺을 수 없습니다.

대체로 기본값을 그대로 쓰는 편이 안전합니다. 8.4가 TLSv1.2에 기본으로 넘기는 암호군은 ECDHE·DHE 키 교환과 GCM·CCM·CHACHA20-POLY1305 조합뿐입니다. 순방향 비밀성과 AEAD 모드를 갖춘 것만 켜 둔 셈입니다. 반대로 aNULL·eNULL·EXPORT·LOW·MD5·DES·RC2·RC4·PSK·SSLv3은 영구 제한입니다. ssl_cert에 지정한 인증서가 제한된 암호군을 쓰면 서버는 암호화 연결 지원을 끈 상태로 기동합니다.

인증 플러그인이 이미 전송 보안을 전제합니다

8.4의 기본 인증 플러그인은 caching_sha2_password입니다. 이 플러그인으로 접속하려면 보안 연결이거나, RSA 키페어로 비밀번호를 교환하는 평문 연결이어야 합니다. 보안 연결이면 RSA 키페어가 필요 없고, 평문 연결이면 비밀번호만 RSA로 암호화해 보냅니다. 서버는 기본적으로 공개키를 클라이언트에 보내지 않으므로, 아무 준비 없이 평문으로 붙으면 이렇게 끊깁니다.

ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password'
reported error: Authentication requires secure connection.

--get-server-public-key로 서버에서 공개키를 받거나 --server-public-key-path로 로컬 사본을 지정하면 통과합니다. 한 번 성공하면 서버 캐시에 항목이 남아 이후 연결은 보안 연결도 RSA 키페어도 요구하지 않습니다. 계정 생성, 비밀번호 변경, RENAME USER, FLUSH PRIVILEGES 뒤의 첫 연결은 다시 둘 중 하나를 요구하고, 캐시는 서버 재시작 사이에 유지되지 않습니다.

TLS를 끄더라도 비밀번호가 평문으로 흐르지는 않는다는 뜻입니다. 다만 그 뒤에 흐르는 쿼리와 결과는 평문입니다.

성능

TLS 오버헤드로 인용할 만한 공개 측정 자료는 마땅치 않습니다. 쿼리 특성과 연결 수립 빈도에 따라 결과가 갈리므로, 도입 판단은 자기 워크로드에서 측정해 정합니다.

비용이 몰리는 지점은 대칭키 암복호보다 핸드셰이크입니다. 그래서 먼저 볼 값이 정해져 있습니다.

이름 역할 기본값
ssl_session_cache_mode 서버측 세션 캐시와 세션 티켓 발급 ON
ssl_session_cache_timeout 세션 재사용 허용 기간(초) 300, 최대 84600
Ssl_sessions_reused 세션을 재사용했으면 1, 아니면 0 세션 상태 변수

두 시스템 변수를 바꾼 값은 ALTER INSTANCE RELOAD TLS를 실행하거나 서버를 재시작한 뒤에 적용됩니다.

측정은 구간을 나눠서 합니다.

  • 연결을 재사용하는 정상 상태의 응답시간 분포와 서버 CPU 사용량
  • 연결을 새로 맺을 때의 지연. 커넥션 풀이 없거나 자주 비는 워크로드가 여기서 드러납니다
  • 재시작이나 페일오버 직후처럼 연결이 한꺼번에 몰리는 구간

커넥션 풀로 연결을 재사용하면 핸드셰이크 비용은 대부분 지나갑니다. 요청마다 접속을 새로 맺는 구조라면 TLS보다 그 구조를 먼저 봅니다.

6. 의사결정 가이드

  1. 위험 평가 — 데이터 민감도는 어느 정도인가, 이 구간이 고시 제2조제12호가 말하는 내부망인가, 인증정보가 이 연결로 흐르는가
  2. 조문 판정 — 제7조제1항과 제4항 중 무엇에 걸리는지 확인하고 판단 근거를 기록으로 남깁니다
  3. 성능 측정 — 5절 항목을 테스트 환경에서 재 봅니다. 감당하기 어려운 수치가 나오면 연결 재사용 구조를 먼저 고칩니다
  4. 대안 검토 — IPsec이나 서비스 메시처럼 네트워크 계층에서 암호화하는 방식이 더 맞는지 봅니다. 이쪽은 애플리케이션 코드와 DB 계정 설정을 건드리지 않습니다
  5. 단계적 적용 — 서버에서 암호화 지원을 켜고, 클라이언트를 --ssl-mode=REQUIRED 이상으로 옮긴 뒤, 평문 연결이 남지 않았는지 세션 단위로 확인하고 마지막에 require_secure_transport를 켭니다

7. 결론

내부망 MySQL 연결에 TLS가 필요한지는 한 문장으로 답하기 어렵습니다. 다만 판단을 흐리게 만드는 두 가지는 정리할 수 있습니다.

첫째, 조문이 직접 요구하지 않는다는 것과 켜지 않아도 된다는 것은 다릅니다. 고시 제7조제4항은 인터넷망 구간을 전제하므로 내부망은 그 범위 밖이지만, 제7조제1항은 인증정보에 대해 구간을 제한하지 않습니다.

둘째, 성능 부담은 대개 막연하게 추정됩니다. 남의 수치로 결정할 문제가 아니라, 연결 재사용 패턴을 확인하고 자기 워크로드에서 재보면 대부분 답이 나옵니다.

내부망이 통제돼 있고 다른 계층의 통제가 갖춰져 있다면 TLS 없이 운영하는 선택도 성립합니다. 그때 남길 것은 설정이 아니라 판단의 근거입니다. 어느 조문에 걸리는지, 어느 통제로 대체했는지, 무엇을 측정했는지를 남기면 다음 담당자가 같은 질문을 처음부터 다시 하지 않습니다.

Advertisement