문득 서버 보안과 관련해서 리서치하던 중 아래의 주제를 발견했다.
백엔드 서버와 DB 서버 간의 데이터를 주고받을 때, SSL/TLS 암호화가 필요할까?
지금까지의 경험 상 web 계층에서 HTTPS 암호화를 위해서의 개념으로만 생각했지, db 계층까지 생각해보지는 않았어서 고민을 하고 정리해보게 됐다.
Q. 우선 SSL/TLS 암호화를 왜 사용할까??
당연히 두 통신 대상 간에 주고받는 데이터를 암호화하여 누군가에게 노출하지 않고 안전하게 주고받기 위해서이다.
암호화하지 않으면 네트워크 트래픽을 엿보는 스니핑(Sniffing)/스푸핑(Spoofing) 공격으로 요청에 담겨진 모든 데이터가 노출되게 된다..
기본적으로 웹 계층에서는 클라이언트와 서버 간의 TCP 연결을 하기 위해 3-way handshake를 하고 SSL/TSL 연결을 한다.
기존의 RSA 방식의 서버 공개키 교환 방식은 서버의 개인키가 유출되게 되면 과거에 수집된 패킷들도 모두 복호화하여 지금까지들의 데이터를 모두 볼 수 있다. 따라서 최신 TLS 통신에서는 서버와 클라이언트 각각 임시 키를 만들어 디피 헬만(ECDHE)이라는 수학적 방법으로 동일한 세션 키를 만든다.
(이전에 정리한 내용은 RSA 기반 방식이었다.)
웹에서의 HTTPS, SSL/TLS 암호화 과정
1. 클라이언트가 서버에게 ClientHello 요청을 한다.
- TLS 버전, 클라이언트 무작위 nonce, 암호화 알고리즘, 압축 방식 등 + 클라이언트 ECDHE 공개키
2. 서버는 ServerHello와 Certificate, Server Key Exchange를 응답한다.
- ServerHello: ClientHello에서 서버 무작위 nonce, 사용할 암호화 알고리즘과 압축 방식
- Certificate: CA에서 발급한 인증서 ('서버 공개키'를 CA 개인키로 서명한 것)
- Server Key Exchange: 중간자 공격을 막기 위해, 서버 ECDHE 공개키를 서버 개인키로 서명
3. 클라이언트는 브라우저에 내장된 CA 공개키로 서버의 공개키를 얻고, PMS / Session Key를 생성한다.
- 인증서에서 얻은 서버 공개키로, 서버 ECDHE 공개키 획득
- 클라이언트 ECDHE 개인키 + 서버 ECDHE 공개키를 조합하여 PMS(Pre-Master Secret) 생성
- PMS + 각 nonce 를 조합하여 세션 키 생성
4. 서버도 PMS / Session Key를 생성한다.
- 클라이언트 ECDHE 공개키 + 서버 ECDHE 개인키를 조합하여 PMS(Pre-Master Secret) 생성
- PMS + 각 nonce 를 조합하여 세션 키 생성
5. 클라이언트가 "Change Cipher Spec" 메시지와 "Finished"를 서버에 전송한다.
- 생성된 세션키로 암호화하여 보낸다.
6. 서버도 마찬가지로 "Change Cipher Spec" 메시지와 "Finished"를 클라이언트에게 전송한다.
7. 서버와 클라이언트가 서로의 "Finished" 메시지를 세션키로 복호화가 가능하면, TLS handshake가 종료된다.
- 세션키로 데이터를 암호화하여 안전하고 빠른 통신이 가능하게 된다.
Q. 그럼 백엔드 애플리케이션에서 데이터베이스 접근 시에도 SSL/TLS 암호화가 필요할까?
백엔드 애플리케이션과 데이터베이스가 동일한 서버에서 실행된다면 필요가 없을 수 있다.
위의 SSL/TLS 암호화에 대한 이야기를 잘 보았다면 "두 통신 대상 간에 주고받는 데이터"를 암호화하는 것인데, 동일한 서버에서 띄워지므로 백엔드에서 데이터베이스에 접근할 때 `localhost`, `127.0.0.1` 과 같은 내부 루프백 주소로 요청을 보내면 된다.
하지만 백엔드와 DB가 서로 다른 서버에서 실행되는 경우 네트워크망을 거쳐 통신이 되기 때문에 스니핑/스푸핑 공격이 가능해진다. 따라서 이 때는 SSL/TLS 암호화를 하는 것이 안전하게 커넥션을 연결하고 유지할 수 있다.
JDBC URL 예시 & SSL 관련 옵션
자바 백엔드 애플리케이션에서 연결하고자 하는 DB 주소인 `jdbc-url`을 다음과 같이 정의할 수 있다.
jdbc:mysql://localhost:3306/test_db?allowPublicKeyRetrieval=true&useSSL=false
여기에서 `useSSL=false` 옵션을 보면 애플리케이션과 DB 간의 통신을 할 때 SSL/TLS 암호화를 사용하지 않겠다는 의미이다. 이처럼 암호화를 하지 않으면 데이터베이스 인증 정보(ID/PW), 쿼리, 쿼리 응답 값이 고스란히 노출될 수 있다. (서로 다른 서버에 구축되었을 시)
추가로 `allowPublicKeyRetrieval=true` 옵션으로 약간은 커버가 될 수 있다. 이 옵션은 애플리케이션 드라이버(ex. JDBC 드라이버)가 DB 서버의 공개키를 요청해 받아올 수 있도록 허용하는 설정이다. 애플리케이션은 이렇게 받아온 공개키로 DB 비밀번호를 암호화해 전송할 수 있게 된다.
cf. false일 경우 DB 서버는 클라이언트에게 공개키를 주지 않고, 다음과 같은 에러가 난다.
Public Key Retrieval is not allowed
하지만 이 방식은 해커가 중간에 끼어들어 악성 공개키를 제공하면 비밀번호가 유출될 위험이 있어, 백엔드와 DB가 서로 다른 서버에 있을 경우 SSL/TLS 암호화를 사용하는 것을 권장한다!
참고문서
SSL Handshake 동작 과정
- https://wch-0625.tistory.com/82#2.%20SSL%2FTLS%EC%9D%B4%20%EB%AD%94%EA%B0%80%EC%9A%94%3F-1-13
토스, TLS(Transport Layer Security)
- https://docs.tosspayments.com/resources/glossary/tls
Reddit, 데이터베이스와 백엔드 서비스 간의 SSL/TLS
MySQL의 TLS/SSL: 내부 네트워크에서 정말 필요할까?
'DB' 카테고리의 다른 글
| MariaDB 백업 성능 비교하기 - mysqldump, mariabackup (0) | 2025.03.31 |
|---|---|
| [DB] 게시판 - 파일 테이블 설계 (0) | 2024.12.22 |
| [MySQL] View Processing Alogrighms (MERGE vs. TEMPTABLE) (3) | 2024.10.11 |
| [MySQL] Partition 3. DATE 기반 월별 파티션 구현 (0) | 2024.08.05 |
| [MySQL] Partition 2. 적용하기 전 개념 정리 (0) | 2024.08.02 |