요약

  • 2018년 8월 나온 RFC 8446은 TLS 1.3의 실제 버전을 supported_versions 확장에서 협상한다. 옛 legacy_version 필드는 TLS 1.2 번호인 0x0303을 유지하므로 이 필드만 보면 연결 버전을 잘못 읽는다.
  • TLS 1.3 지원 서버가 TLS 1.2를 선택하면 ServerHello.random의 끝 여덟 바이트를 44 4F 57 4E 47 52 44 01로 써야 한다. TLS 1.1 이하라면 마지막 바이트는 00이다. TLS 1.3 클라이언트는 옛 버전 응답에서 어느 표식이든 발견하면 핸드셰이크를 중단해야 한다.
  • 2015년 RFC 7507은 다른 경로를 다뤘다. 클라이언트가 낮은 버전으로 재시도할 때 TLS_FALLBACK_SCSV를 실어 보내고, 높은 버전을 지원하는 서버가 부적절한 후퇴를 거절한다. IANA에 그 값과 경보가 등록돼 있다.

문 앞의 오래된 번호

TLS 1.3의 ServerHello.legacy_version만 읽는 감시 장비는 연결을 TLS 1.2라고 기록할 수 있다. RFC 8446은 그 칸에 0x0303을 남기고 실제 결정을 supported_versions로 옮겼다. 이름의 “legacy”는 그 칸의 역할을 드러낸다. 예전 전송 형태를 유지하기 위한 번호이지 양쪽 끝이 사용하는 보안 프로토콜의 결론이 아니다. 이름표를 실행 상태로 오인하면 보안 기록부터 틀어진다.

오래된 이름표를 남긴 이유는 통신 경로에 있었다. 일부 중간 장비가 익숙한 핸드셰이크 형식만 이해하고 새로운 패턴을 만나면 연결을 깨뜨렸다. RFC 8446 부록 D.4의 호환 모드는 TLS 1.3 교환을 TLS 1.2처럼 보이게 한다. 클라이언트가 빈 값이 아닌 세션 ID를 보내고, 더미 ChangeCipherSpec 메시지가 나타날 수 있다. 양쪽 끝이 확장에서 TLS 1.3을 선택했다면 실제 암호 프로토콜은 TLS 1.3이다. 낡은 옷은 길목의 장비를 위한 것이다.

그 장비는 보안 계약의 당사자가 아니면서 사실상의 거부권을 행사했다. 낯선 패킷을 떨어뜨릴 수 있으면 신버전은 외형을 바꿔야 한다. 표준은 전송 중 보이는 모양을 조정했지만, 버전을 결정하는 권한까지 중간 장비에 넘기지 않았다. 어떤 번호가 눈에 띄는지와 어떤 값이 양쪽 끝의 결정을 나타내는지는 별개의 문제다.

반대편에 남긴 여덟 바이트

호환성보다 더 날카로운 위험은 경로상의 공격자가 연결을 구버전으로 몰아가는 일이다. TLS 1.3 지원 서버가 TLS 1.2로 협상하면 ServerHello.random의 마지막 여덟 바이트를 44 4F 57 4E 47 52 44 01로 채워야 한다. TLS 1.1 이하를 선택하면 앞의 일곱 바이트는 같고 마지막은 00이다. 앞의 일곱 바이트는 ASCII로 DOWNGRD다. 마지막 한 바이트가 낮아진 버전 범위를 가른다.

TLS 1.3 클라이언트는 TLS 1.2 이하를 알리는 ServerHello를 받으면 두 패턴을 확인한다. 일치할 경우 illegal_parameter 경보와 함께 핸드셰이크를 끝내야 한다. 더 높은 버전을 지원하는 서버가 실제로는 낮은 버전에 머물렀다는 사실을 알았기 때문이다. 옛 연결이 성공한다는 사실만으로, 양쪽이 쓸 수 있는 새 버전을 쓰지 않은 이유는 설명되지 않는다.

표식의 효과에는 범위가 있다. RFC 8446은 이를 Finished 교환에 더해지는 제한적인 보호라고 설명한다. 임시 키를 사용하는 암호 조합에서는 구버전 핸드셰이크의 서명이 양쪽 난수에 묶이지만, 정적 RSA는 같은 보호를 제공하지 않는다. 이미 배포된 많은 TLS 1.2 구현도 새 규칙대로 움직이지 않을 수 있다고 RFC는 적는다. 서버가 쓰고 클라이언트가 읽는 곳에서만 여덟 바이트가 경보가 된다.

클라이언트 재시도의 별도 신호

2015년 RFC 7507은 클라이언트가 낮은 버전으로 다시 시도하는 문제를 다뤘다. 핸드셰이크 실패 뒤 호환성을 위해 버전을 낮추는 클라이언트가 있었고, 공격자가 실패를 유발하면 재시도를 밀어붙일 수 있었다. 클라이언트가 TLS_FALLBACK_SCSV를 실어 보내면 높은 버전을 지원하는 서버는 inappropriate_fallback으로 거절한다. IANA TLS 매개변수 등록부에 해당 신호와 경보가 실려 있다.

2015년의 신호는 낮은 버전으로 재시도하는 클라이언트가 보낸다. 2018년의 표식은 신버전을 지원하는 서버가 자신의 난수에 넣는다. 각각 협상의 다른 순간, 다른 주체가 가진 지식을 드러낸다. 둘을 막연한 “다운그레이드 방지”로 뭉치면 누가 언제 더 높은 버전의 가능성을 알고 있었는지 놓친다.

낡은 옷의 주인

부록 D.4는 이미 깔린 장비가 새 규격의 모양을 바꾸게 만든 기록이다. TLS 1.3은 그 장비에 익숙한 외형을 보여 줬다. 서버 난수의 끝에는 다른 선을 그었다. 호환성을 위한 외양이 강제로 구버전을 택하는 데 대한 묵인이 되어서는 안 된다. 양쪽 끝은 실제 버전 결정권을 유지하고, 클라이언트는 유능한 서버의 낮은 선택을 확인할 단서를 받았다.

공식 문서와 등록부는 규칙을 보여 준다. 특정 연결에서 호환 모드와 표식이 사용됐는지는 날짜가 있는 패킷 기록과 클라이언트·서버 구현 기록으로 확인해야 한다. 2018년의 설계는 분명하다. TLS 1.3은 진짜 버전을 확장에 두고 낡은 번호를 문 앞에 남겼으며, 새 버전을 쓸 수 있는 서버가 물러설 때 난수 끝에 경보를 달았다.