error:1408F10B:SSL routines:ssl3_get_record:wrong version number
이름 때문에 SSLv3 문제로 오해하기 쉽지만 함수 이름일 뿐이다. 실제 뜻은 받은 바이트가 TLS 레코드로 해석되지 않는다 이며, 원인은 다음 순서로 흔하다.
https://host:80, 평문 HTTP 서버에 openssl s_client, LDAP 389 에 ldaps:// 로 붙는 경우가 모두 해당한다. 서버가 평문 응답을 보내는데 클라이언트가 그것을 TLS 레코드로 읽으려다 실패한다.https 로 전달하는 경우다.# 포트가 TLS 인지 먼저 본다
openssl s_client -connect <host>:<port> </dev/null 2>&1 | head -20
# StartTLS 프로토콜은 옵션을 준다
openssl s_client -connect smtp.example.com:587 -starttls smtp
openssl s_client -connect ldap.example.com:389 -starttls ldap
# 평문이면 그냥 붙어 응답이 읽힌다
curl -sv http://<host>:<port>/ 2>&1 | head
프로토콜 자체가 맞지 않으면 다른 메시지가 난다.
sslv3 alert handshake failure
no protocols available
unsupported protocol
서버가 어떤 버전을 받는지 하나씩 확인한다.
for v in tls1 tls1_1 tls1_2 tls1_3; do
printf '%-8s ' "$v"
openssl s_client -connect example.com:443 -servername example.com -"$v" </dev/null 2>&1 \
| grep -m1 -E 'Protocol|handshake failure|no protocols'
done
최근 배포판은 시스템 정책에서 오래된 버전을 아예 막는다. RHEL 8 이상은 update-crypto-policies 가 TLS 1.0 · 1.1 을 기본으로 차단하므로, 옛 장비에 붙어야 한다면 전역 정책을 내리기보다 그 애플리케이션에만 예외를 주는 편이 낫다.
update-crypto-policies --show # DEFAULT, LEGACY 등
openssl ciphers -v 'DEFAULT' | head
SSLv3 는 POODLE 이후 폐기됐고(RFC 7568) 최신 OpenSSL 은 기본 빌드에서 제외한다. 이 메시지가 뜨면 SSLv3 를 되살릴 것이 아니라 상대를 TLS 1.2 이상으로 올려야 한다. 재컴파일로 SSLv3 를 켜는 것은 취약점을 되돌리는 선택이다.
정말 옛 장비와 통신해야 한다면 그 통신만 전용 프록시 · 전용 호스트로 격리하고 망 접근을 제한하는 쪽이 안전하다.
curl --tlsv1.2 --tls-max 1.2 -v https://example.com
curl -v --noproxy '*' https://example.com # 프록시 개입 배제
--sslv3 옵션은 최신 curl 에서 동작하지 않으며 쓸 이유도 없다. 사내 프록시가 중간에서 TLS 를 끊는 환경이면 프록시를 배제한 결과와 비교해야 원인이 갈린다.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5
SSLHonorCipherOrder off
TLS 1.3 은 암호군을 클라이언트 · 서버가 협상하는 방식이 달라 ssl_prefer_server_ciphers 의 의미가 사라진다. 요즘은 서버 우선순위를 강제하지 않는 것이 권장이다.