목차
- 보안 점검 리포트 한 장에서 시작된 일
- npm audit fix로는 안 되는 세계
- 4.0.0 릴리즈 노트를 읽었다
- 스테이징에 올려본 48시간
- 디펜던시 체인이라는 현실
- 롤백, 그리고 타임라인 재설정
- 내가 세운 마이그레이션 타임라인
- 이번에 배운 것 — 프론트엔드와 다른 점
보안 점검 리포트 한 장에서 시작된 일
컨테이너 이미지 보안 스캔을 돌렸더니 OpenSSL 관련 CVE 경고가 4건 떴다. 전부 3.x 대에서 이미 패치된 항목이었는데, 마침 OpenSSL 4.0.0이 정식 릴리즈됐다는 소식이 들렸다. 2026년 4월 기준으로 OpenSSL 4.0.0은 출시 직후 상태고, 프로젝트에서 쓰고 있던 건 3.2 계열이었다. 메이저 버전이 올라갔으니 지금 올릴지, 3.x 패치만 받으며 기다릴지 판단해야 했다.
프론트엔드에서 백엔드로 전환한 지 2년 정도 됐는데, 시스템 레벨 라이브러리의 메이저 업그레이드는 이번이 처음이었다. npm이나 yarn이면 audit fix 한 줄로 끝나는 일이 많았는데, OpenSSL은 그런 세계가 아니다. OS 패키지 매니저, 컨테이너 베이스 이미지, 그 위에 올라가는 언어별 바인딩까지 전부 엮여 있다. 이 글은 그 과정을 시간순으로 풀어본 기록이다.
npm audit fix로는 안 되는 세계
프론트엔드와 백엔드의 의존성 관리 차이
프론트엔드 시절에는 의존성 문제가 대부분 node_modules 안에서 끝났다. package-lock.json 고치고 버전 올리면 됐다. 깨지더라도 빌드 타임에 바로 에러가 나니까 피드백 루프가 짧았다.
시스템 라이브러리는 다르다. OpenSSL은 애플리케이션 코드에서 직접 import하는 게 아니라, Python의 cryptography, ssl 모듈, Node.js의 tls 모듈, 심지어 curl이나 git까지 내부적으로 의존한다. 메이저 버전을 올리면 그 위에 쌓인 모든 레이어가 영향을 받는다.
의존성 트리의 깊이 문제
하나의 FastAPI 서비스 기준으로 OpenSSL에 의존하는 경로를 추적해봤다.
uvicorn → ssl (stdlib) → _ssl (C extension) → libssl.so → OpenSSL
httpx → httpcore → h11/h2 → ssl
cryptography → _openssl (cffi binding) → libcrypto.so → OpenSSL
psycopg[binary] → libpq → libssl.so → OpenSSL
이 중에서 cryptography 라이브러리가 가장 민감하다. OpenSSL API를 직접 바인딩하고 있어서, OpenSSL 메이저 버전이 바뀌면 호환성이 깨질 가능성이 높다. 실제로 3.0 전환 때도 cryptography가 가장 늦게 대응했던 기억이 있다 — 정확히는 직접 겪은 건 아니고, 당시 백엔드 팀 슬랙 채널에서 한참 논의되던 걸 읽었다.
4.0.0 릴리즈 노트를 읽었다
OpenSSL 4.0.0의 변경사항은 공식 릴리즈 노트(출처: OpenSSL 공식 블로그)에서 확인할 수 있다. 2026년 4월 기준으로 파악한 주요 변경점을 정리하면 이렇다.
제거된 API들
3.0에서 deprecated 처리됐던 1.1.1 시대의 레거시 API가 4.0에서 완전히 제거됐다. EVP가 아닌 low-level 암호화 함수들 — RSA_public_encrypt(), DES_ecb_encrypt() 같은 것들이 대표적이다. 3.0 전환 때 EVP 인터페이스로 마이그레이션하라는 경고가 떴었는데, 그때 무시하고 -DOPENSSL_SUPPRESS_DEPRECATED 플래그로 경고만 끈 프로젝트들은 4.0에서 빌드 자체가 안 된다.
FIPS 모듈 변경
FIPS 프로바이더 구조가 바뀌었다. 3.x에서는 FIPS 모듈을 별도로 빌드해서 openssl.cnf에 등록하는 방식이었는데, 4.0에서는 프로바이더 로딩 메커니즘이 개편됐다. 금융권이나 공공 프로젝트처럼 FIPS 인증이 필수인 환경에서는 이 부분만으로도 마이그레이션 일정이 상당히 늘어난다.
포스트 양자 암호 지원
ML-KEM(구 CRYSTALS-Kyber)과 ML-DSA(구 CRYSTALS-Dilithium) 알고리즘이 기본 포함됐다. NIST가 2024년에 표준화한 포스트 양자 알고리즘들인데, 3.x에서는 별도 프로바이더나 OQS(Open Quantum Safe) 포크를 써야 했다. 4.0에서는 기본 빌드에 들어가 있어서, TLS 1.3 핸드셰이크에서 하이브리드 키 교환을 바로 쓸 수 있다.
솔직히 포스트 양자 암호는 아직 대부분의 서비스에서 급하지 않다. 양자 컴퓨터가 RSA-2048을 깨는 시점이 언제인지는 아무도 모르고, "harvest now, decrypt later" 위협이 현실적인 분야는 군사·정보 기관 정도다. 일반적인 웹 서비스라면 이걸 이유로 4.0에 올라갈 필요는 없다.
스테이징에 올려본 48시간
릴리즈 노트를 읽고 나서 판단이 필요했다. 우리 서비스는 deprecated API를 직접 쓰지 않고, FIPS도 필요 없으니 의외로 괜찮을 수 있겠다 싶었다. 그래서 스테이징 환경에 먼저 올려보기로 했다.
베이스 이미지 교체
첫 번째로 한 건 Docker 베이스 이미지 변경이었다. 기존에 python:3.12-slim-bookworm을 쓰고 있었는데, Debian bookworm의 기본 OpenSSL은 3.0.x다. 4.0.0을 넣으려면 선택지가 몇 가지 있다.
# 방법 1: 소스 빌드 (비추천 — 빌드 시간 체감상 3~4배 증가)
RUN wget https://www.openssl.org/source/openssl-4.0.0.tar.gz && \
tar xzf openssl-4.0.0.tar.gz && \
cd openssl-4.0.0 && \
./Configure --prefix=/usr/local/ssl --openssldir=/usr/local/ssl && \
make -j$(nproc) && \
make install # 이후 LD_LIBRARY_PATH 설정 필요
# 방법 2: 배포판 패키지 사용 (추천 — 있다면)
# 2026년 4월 기준 Debian trixie/sid에 4.0.0 패키지가 올라와 있는지 확인 필요
방법 1로 시도했다. 소스 빌드 자체는 문제없이 됐는데, 그다음이 문제였다.
cryptography 라이브러리의 벽
pip install cryptography
이 한 줄에서 막혔다. cryptography 43.x 버전(2026년 4월 기준 최신 안정 릴리즈)이 OpenSSL 4.0.0의 일부 API 변경을 아직 반영하지 않은 상태였다. 빌드 과정에서 이런 에러가 나왔다.
error: OpenSSL 4.0.0 is not yet supported. You are linking against
OpenSSL 4.0.0, but this version of cryptography requires OpenSSL
>= 3.0.9, < 4.0.0
cryptography 라이브러리가 OpenSSL 버전 상한을 걸어둔 거다. 보안에 민감한 라이브러리답게 검증 안 된 버전은 아예 차단한다. GitHub 이슈를 찾아보니 4.0.0 지원 작업이 진행 중이라는 코멘트는 있었는데, 정식 릴리즈 일정은 명시되지 않았다.
이 시점에서 이미 절반은 답이 나왔다. Python 백엔드 서비스에서 cryptography를 안 쓰는 경우는 거의 없으니까.
디펜던시 체인이라는 현실
cryptography만 문제가 아니었다. 48시간 동안 스테이징에서 부딪힌 것들을 시간순으로 나열하면 이렇다.
빌드 단계에서 바로 터진 것: cryptography 버전 상한 에러. 위에서 설명한 그대로다. 그 다음으로 psycopg[binary]의 바이너리 휠이 OpenSSL 3.x 기준으로 빌드되어 있어서, 4.0.0 환경에서 libssl.so.3을 찾지 못하는 문제가 나왔다. psycopg[c]로 바꿔서 소스 빌드하면 되긴 하는데, CI 빌드 시간이 눈에 띄게 늘어난다.
런타임에서 발견된 것: Python 표준 라이브러리의 ssl 모듈은 OpenSSL 4.0.0과 호환됐다. CPython 3.12가 OpenSSL 3.0+ API만 사용하도록 이미 정리된 상태이기 때문이다. 이건 다행이었다.
간접적으로 영향받은 것: curl이 4.0.0 링크 상태에서 일부 TLS 옵션의 기본값이 바뀌어서, 헬스체크 스크립트가 실패했다. 인증서 검증 관련 기본 동작이 더 엄격해진 것으로 보였는데, 이 부분은 깊이 파보지 못했다.
| 구분 | 라이브러리 | OpenSSL 4.0.0 호환 (2026-04 기준) | 비고 |
|---|---|---|---|
| 빌드 차단 | cryptography 43.x | ✗ | 버전 상한 에러 |
| 빌드 차단 | psycopg[binary] | ✗ | 바이너리 휠 링크 불일치 |
| 호환 | CPython ssl 모듈 | ✓ | 3.0+ API만 사용 |
| 호환 | uvicorn/httpx | ✓ | ssl 모듈 경유 |
| 미확인 | curl (helthcheck) | △ | TLS 기본값 변경 |
프론트엔드에서 left-pad 사건이나 node-ipc 사건을 보면서 "의존성 지옥"이라는 말을 많이 했었는데, 시스템 라이브러리 수준의 의존성은 차원이 다르다. npm 패키지는 최소한 격리가 되지만, OpenSSL은 OS 전체에 영향을 준다.
롤백, 그리고 타임라인 재설정
48시간 만에 스테이징을 원래 상태로 되돌렸다. 롤백 자체는 Docker 이미지 태그를 이전 것으로 바꾸면 되니까 5분이면 끝났다. 문제는 "그래서 언제 올리느냐"에 대한 답을 내야 했다는 거다.
당장 올려야 하는 경우
OpenSSL 3.x에서 아직 패치되지 않은 심각한 취약점(Critical CVE)이 나오면 이야기가 달라진다. 4.0.0에서만 수정된 보안 이슈가 있다면 디펜던시가 깨지더라도 올려야 한다. 2026년 4월 기준으로 그런 상황은 아니다. 3.2 계열은 여전히 보안 패치를 받고 있고, OpenSSL 프로젝트의 릴리즈 정책에 따르면 3.x LTS는 당분간 지원이 유지된다.
기다려도 되는 경우
대부분의 웹 서비스가 여기에 해당한다. 핵심 조건은 두 가지다.
첫째, 디펜던시 체인의 주요 라이브러리들이 4.0.0을 공식 지원할 때까지 기다리는 게 안전하다. cryptography, psycopg, 쓰고 있는 HTTP 클라이언트 라이브러리의 릴리즈 노트를 주시하면 된다.
둘째, 사용 중인 Linux 배포판의 패키지 저장소에 4.0.0이 올라오는 시점. 소스 빌드는 가능하지만 유지보수 비용이 크다. 배포판 패키지가 있으면 보안 패치도 apt/dnf로 받을 수 있어서 운영이 훨씬 편하다.
포스트 양자 암호가 필요한 경우
아까 급하지 않다고 했지만 예외가 있다. 장기 보존 데이터를 다루는 서비스 — 의료 기록, 법률 문서, 정부 데이터 같은 곳은 "harvest now, decrypt later" 위협을 진지하게 고려해야 한다. 이런 환경이라면 4.0.0의 ML-KEM/ML-DSA 네이티브 지원이 업그레이드 동기가 될 수 있다.
내가 세운 마이그레이션 타임라인
롤백 후에 세운 계획은 이렇다. 개인 프로젝트가 아니라 팀 서비스이니까, 단계별로 체크포인트를 뒀다.
1단계 (현재): OpenSSL 3.2.x 유지. 보안 패치는 OS 패키지 업데이트로 적용. cryptography 라이브러리의 GitHub 릴리즈를 watch 해둔다.
2단계 (cryptography가 4.0.0 지원하면): 스테이징에서 다시 테스트. 이번에는 전체 E2E 테스트를 돌리고, 특히 TLS 핸드셰이크, 인증서 갱신, DB 연결 쪽을 집중 확인한다.
3단계 (배포판 패키지 나오면): 프로덕션 마이그레이션. 카나리 배포로 트래픽 일부만 먼저 전환하고, 에러율 모니터링 후 전체 전환.
이걸 구체적으로 언제 실행하게 될지는 모르겠다. cryptography의 지원 속도에 달려 있는데, 3.0 전환 때는 릴리즈 후 수개월이 걸렸다.
이번에 배운 것 — 프론트엔드와 다른 점
프론트에서 백엔드로 넘어오면서 가장 적응이 안 됐던 게 "기다리는" 판단이다.
프론트엔드에서는 새 버전 나오면 일단 올려보고, 깨지면 고치는 게 합리적이었다. 빌드 타임에 대부분 잡히고, 런타임 이슈도 브라우저 개발자 도구에서 바로 확인할 수 있었으니까. 롤백도 git revert 한 줄이면 됐다.
시스템 라이브러리는 "일단 올려본다"가 통하지 않는다. 정확히는, 스테이징에서는 해볼 수 있지만 프로덕션에 갈 때는 훨씬 보수적으로 가야 한다. 이유는 간단하다. 영향 범위를 예측하기 어렵기 때문이다. OpenSSL을 올리면 그 서버에서 돌아가는 모든 프로세스가 영향을 받는다. 컨테이너로 격리되어 있어도, 같은 이미지를 쓰는 서비스가 여러 개면 전부 테스트해야 한다.
지금 돌이켜보면 이번 시도가 시간 낭비는 아니었다. 어차피 언젠가는 올려야 하고, 어디서 깨지는지 미리 파악해둔 것 자체가 자산이다. cryptography 버전 상한 에러, psycopg[binary] 링크 문제, curl 기본값 변경 — 이 세 가지를 알고 있으니까 다음 시도 때는 훨씬 빠르게 움직일 수 있다.
# 현재 시스템의 OpenSSL 버전 확인
openssl version -a
# Python이 링크된 OpenSSL 버전 확인
python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"
# 컨테이너 이미지 내 OpenSSL 확인
docker run --rm python:3.12-slim-bookworm openssl version
당장 할 수 있는 건 이 세 줄이다. 지금 쓰고 있는 환경의 OpenSSL 버전부터 확인하고, 3.x 보안 패치가 최신인지 점검하는 것. 4.0.0 업그레이드는 cryptography 라이브러리가 공식 지원을 선언한 뒤에 시작해도 늦지 않다. 아직 주요 디펜던시들의 4.0.0 호환성 검증이 완료되지 않은 상태라, 성급한 전환보다는 3.x 패치 유지가 현실적인 선택이다.
관련 글
- OpenSSL 4.0.0 프로덕션 마이그레이션, 지금 올려도 되는가 – OpenSSL 메이저 업그레이드는 라이브러리 하나가 아니라 디펜던시 체인 전체를 흔드는 작업이다. 4.0.0 프로덕션 적용 타이밍을 3.0…
- 로컬 LLM 보안 운영 — Ollama로 사내 데이터 유출 없이 AI 쓰는 법 – 사내 외부 LLM API가 차단된 뒤 Ollama로 로컬 LLM 보안 운영 환경을 구축했다. 기본 설정의 보안 구멍부터 네트워크 격리, 접…
- Trivy로 컨테이너 이미지 취약점 스캔을 CI/CD에 붙인 3개월 회고 – 프론트엔드에서 백엔드로 넘어오고 나서 처음 맡은 컨테이너 보안 스캔 자동화. Trivy를 GitHub Actions에 붙이면서 겪은 시행착…