요약

  • TLS 1.2는 기존 연결 안에서 새 핸드셰이크를 수행할 수 있었지만, 그 새 핸드셰이크가 어떤 이전 핸드셰이크와 애플리케이션 스트림을 이어받은 것인지 증명하지는 않았습니다.
  • RFC 5746은 재협상을 저장된 Finished 메시지 값에 묶었고, 호환성 신호를 통해 구형 확장에 민감한 배포 환경도 안전하게 지원을 알릴 수 있게 했습니다. 이후 TLS 1.3은 재협상을 완전히 제거했습니다.

두 개의 정상적인 핸드셰이크, 하나의 거짓 대화

이 공격은 정당한 연결로 시작되었습니다. 다만 그 연결은 피해자의 것이 아니었습니다. 공격자가 서버와 TLS를 맺고 선택한 애플리케이션 바이트를 전송했습니다. 그런 다음 공격자는 피해자 클라이언트의 핸드셰이크가 이미 보호된 그 채널을 통과하도록 허용했습니다. 클라이언트 입장에서는 서버와의 초기 핸드셰이크처럼 보였습니다. 서버 입장에서는 공격자의 연결에 대한 재협상처럼 보였습니다.

그 이후 공격자는 피해자의 보호된 트래픽을 읽을 수 없었습니다. 그러나 이 제약은 이미 전달된 접두부를 되돌리지 못했습니다. 서버는 공격자의 바이트 뒤에 피해자의 인증된 바이트가 이어지는 것을 보고, 양쪽을 하나의 애플리케이션 상호작용으로 취급할 수 있었습니다. 예를 들어 HTTPS에서 인증 전 단계와 인증 후 단계를 구분하지 않는 애플리케이션은 공격자가 선택한 입력을 클라이언트가 나중에 제공하는 자격 증명이나 쿠키와 연관 지을 수 있었습니다.

이것은 깨진 암호나 위조된 인증서에 관한 이야기가 아니었습니다. TLS 1.2는 클라이언트가 기존 연결 위에서 또 다른 ClientHello를 보내는 것을 허용했고, 재협상 메시지를 보호하기 위해 현재 연결 상태를 사용했습니다. 각 핸드셰이크는 유효한 Finished 메시지로 완료될 수 있었습니다. 빠져 있던 것은 연속성이었습니다. 어느 쪽 종단도 새 핸드셰이크가 상대방이 방금 관찰한 동일한 암호학적 이력의 후계자임을 증명하지 않았습니다.

이 구분 때문에 애플리케이션 계층의 리셋이 TLS 자체를 수리하지 않고도 일부 공격을 제한할 수 있었습니다. 인증되지 않은 명령과 인증된 명령 사이에 엄격한 전환을 두는 프로토콜은 이전 상태를 폐기할 수 있습니다. 다른 프로토콜은 연결을 하나의 바이트 스트림으로 처리할 수 있습니다. RFC 5746은 모든 애플리케이션이 올바른 경계를 재구성할 것이라고 가정하는 대신, 이 모호성을 전송 보안 결함으로 다루었습니다.

이전 Finished 메시지를 상태로 만들기

RFC 5746은 연결별 상태를 세 가지 추가했습니다.secure_renegotiation플래그는 이 수리가 활성화되어 있는지를 기록했습니다. 클라이언트와 서버는 또한 바로 이전 핸드셰이크의 Finished 메시지에서 나온verify_data를 저장했습니다. 이 값들은 어딘가에서 재개될 수 있는 TLS 세션 캐시 항목이 아니라, 살아 있는 연결에 속한 값이었습니다.

새로운renegotiation_info확장(타입0xff01)은 그 이력을 다음 협상으로 운반했습니다. 초기 핸드셰이크에서는 이 확장의renegotiated_connection필드가 비어 있었습니다. 이 빈 값은 선행 핸드셰이크를 묶지 않았고, 양쪽 피어가 보안 재협상 규칙을 이해하고 있음을 알리는 것이었습니다.

재협상 중에는 클라이언트가 저장해 둔client_verify_data를 확장에 넣습니다. 서버는 이를 이 연결을 위해 저장된 값과 대조합니다. 서버는 그런 다음 저장된 클라이언트 값과 서버 값의 연접을 반환하고, 클라이언트는 이를 자신의 이력과 대조합니다. 필수 확장이 없거나 값이 일치하지 않으면 핸드셰이크는 치명적 오류로 끝납니다.

이 비대칭성은 의도된 것이었습니다. 재협상하는 ClientHello는 클라이언트가 본 이전 핸드셰이크에 대한 관점을 증명할 수 있었습니다. 그런 다음 ServerHello는 서버가 본 양쪽 Finished 메시지에 대한 관점을 증명했습니다. 새 핸드셰이크가 완료되면 양쪽 모두 저장된 값을 새 값 쌍으로 교체합니다. 따라서 다음 전환은 막연한 세션 계보가 아니라 바로 그 직전 선행자를 지명하게 됩니다.

편집적으로 표현하면, 프로토콜이 기억하는 법을 배운 것입니다. 더 정확히는, RFC 5746이 시간적 연속성을 인증된 입력으로 만든 것입니다. 올바르게 완료된 핸드셰이크만으로는 보안 재협상에 더 이상 충분하지 않았습니다. 그것은 이 정확한 연결을 위한 올바른 다음 핸드셰이크여야 했습니다.

암호 스위트가 아닌 암호 스위트 코드

이 수리는 배포된 호환성 경계를 넘어야 했습니다. 일부 SSLv3, TLS 1.0 및 TLS 1.1 구현은 사양이 관용적 동작을 요구했음에도, ClientHello가 자신들이 이해하지 못하는 확장을 담고 있으면 잘못되게 실패했습니다. 따라서 새 확장만 보내면 어느 쪽도 아무것도 협상하기 전에 구형 서버로의 연결이 끊어질 수 있었습니다.

RFC 5746은 초기 핸드셰이크를 위한 두 번째 신호를 제공했습니다.TLS_EMPTY_RENEGOTIATION_INFO_SCSV로,0x00,0xFF가 할당되었습니다. 이것은 암호 스위치 목록을 차지했는데, 구형 구현이 알 수 없는 암호 스위트를 안정적으로 무시할 것으로 기대되었기 때문입니다. 이것은 알고리즘 선택이 아니었고 협상된 스위트로 선택될 수 없었습니다. 그 의미는 빈renegotiation_info확장을 제공하는 것과 동등했습니다.

이 호환성 장치는 이후 핸드셰이크에 대한 묶음을 대체하지 않았습니다. 보안 재협상 자체는 확장과 저장된 Finished 값을 사용했습니다. SCSV는 결함이 있을 수 있는 확장 파서에 의존하지 않고 클라이언트가 초기 접촉에서 능력을 알릴 수 있게 해 주었습니다.

그럼에도 이 전환은 정책 선택을 요구했습니다. 서버가 보안 재협상을 인정하지 않으면, 클라이언트는 TLS만으로 그 서버가 취약한지 아니면 재협상 자체를 완전히 거부하는지 알 수 없었습니다. 연결을 거부하면 보호가 최대화되지만 무해한 미갱신 종단과의 상호 운용성을 잃을 수 있었습니다. 계속하면 도달 가능성은 유지되지만 스플라이스 공격이 불가능함을 보장할 수 없었습니다. RFC 5746은 하나의 신호가 구형 서버의 비공개 정책을 드러낼 수 있다고 가장하는 대신, 이 트레이드오프를 문서화했습니다.

수리에서 제거로

TLS 1.3은 다른 아키텍처적 경계를 택했습니다. 재협상을 금지합니다. TLS 1.3을 협상한 서버가 다른 시점에 ClientHello를 받으면unexpected_message얼럿으로 연결을 종료해야 합니다. 재협상 중에 TLS 1.3 ClientHello를 받은 구형 TLS 연결은 이전 프로토콜 버전을 유지해야 합니다. 재협상으로 그 연결이 TLS 1.3으로 업그레이드될 수는 없습니다.

이 제거를 핸드셰이크 이후의 모든 작업의 사라짐과 혼동해서는 안 됩니다. TLS 1.3에는 별도의 키 갱신과 핸드셰이크 이후 인증 메커니즘이 있으며, 현재의 운영 지침은 그것들의 애플리케이션 결과를 독립적으로 다룹니다. TLS 1.2의 경우 RFC 9325는 클라이언트와 서버 모두renegotiation_info를 구현할 것을 요구하고, 서버가 이를 인정하지 않으면 클라이언트가 연결을 종료할 것을 요구합니다. 또한 RFC 5746이 여러 TLS 핸드셰이크와 관련된 모든 공격에 답하는 것은 아닙니다. RFC 9325는 TLS 1.2의 관련 트리플 핸드셰이크 공격 계열에 대해 별도로 extended master secret 수리를 요구합니다.

더 좁은 역사적 결론은 더 강력합니다. RFC 5746은 프로토콜 설계자가 더 이상 암묵적으로 둘 수 없는 속성을 드러냈습니다. 지속되는 연결 안에서 보안 상태가 바뀔 때, 그 전환 자체에 인증된 의미가 필요하다는 것입니다. TLS 1.2는 재협상을 유지하면서 새 핸드셰이크를 마지막 핸드셰이크에 연결했습니다. TLS 1.3은 일반적인 전환을 제거하고 특정 요구를 더 좁은 메커니즘으로 대체했습니다. 두 경우 모두, 연결은 인접한 인증된 조각들이 반드시 하나의 인증된 이야기에 속한다고 애플리케이션이 가정하도록 하는 일을 멈췄습니다.

출처