오류로 끝나는 핸드셰이크는 메시지를 보기 전까지는 공격처럼 느껴지지만, 대부분은 산술이다. 공유된 제안이 없거나, 인증서가 만료되었거나, 패킷이 도착하지 않은 것이다. 다운그레이드 공격은 더 구체적이다. 공격자가 양쪽을 스스로 고르지 않았을 약한 동작으로 밀어붙이고, 남는 증거도 다르다. 모든 실패를 악의로 보면 존재하지 않을 수도 있는 피해자를 찾게 되고, 실제 다운그레이드를 설정 오류로 보면 약한 결과가 그대로 남는다. 두 오류의 비용은 다르고, 값싸게 고칠 수 있는 것은 하나뿐이다.

다운그레이드 공격이 실제로 바꾸는 것

목표는 암호를 깨거나 키를 추측하는 것이 아니다. 협상에 영향을 주어 양쪽이 공격자가 감당할 수 있는 더 오래된 버전이나 더 약한 알고리즘에 머물게 하거나, 한쪽이 다른 쪽이 그것만 지원한다고 믿게 만드는 것이다.

바뀌는 것은 합의이지 인증이 아니다. 공격자가 탐지되지 않고 제안을 바꿀 수 있다면, 양측은 여전히 핸드셰이크를 마치고 서로를 인증하지만, 공격자가 선호하는 매개변수를 쓴다. 그래서 중요한 질문은 협상 자체가 무결성 보호에 포함되는지다.

협상 자체가 인증되어야 하는 이유

TLS 1.3은 그 보호를 명시한다. 더 새로운 버전을 제안한 클라이언트는 조용히 후퇴한 서버를 탐지할 수 있다. 서버의 난수 값은 1.2 이하를 협상할 때 고정 패턴을 가지며, 클라이언트는 그것을 보면 중단해야 한다. 전체 트랜스크립트도 종료 메시지로 덮이므로, 협상 어디를 고쳐도 나중에 실패하고 조용히 지나가지 않는다.

오래된 프로토콜 버전은 설명이 약했고, 폴백 규칙이 중요한 이유가 바로 그것이다. 같은 설정의 TLS 1.0 엔드포인트는 다운그레이드 공격이 아니다. 당신이 허용하기로 선택한 허용된 결과다. 구분은 약한 결과가 보호된 결정에서 왔는지, 중개자가 영향을 줄 수 있는 보호되지 않은 경로에서 왔는지다. 이렇게 표현하면 대부분의 사건은 설정 질문으로 정리된다.

IKEv2는 다르게 동작하지만 같은 생각에 기댄다. 제안은 나중에 양쪽을 인증하는 교환에서 전달되므로, 알고리즘을 다시 쓰는 중간 장치는 인증을 조종하는 대신 깨뜨린다. OpenVPN의 제어 채널은 TLS로 협상을 보호한 뒤 그 채널로 데이터 채널의 암호를 합의한다. 그래서 제어 채널 로그 줄이 흥미롭다.

설정된 호환성은 공격이 아니다

관리자는 문서화된 클라이언트를 지원하기 위해 의도적으로 오래된 선택을 허용할 수 있고, 그 결정은 설정에 드러난다. 공격자는 그 설정을 만들 수 없고, 제안에 영향을 줄 수 있는 경로를 악용할 뿐이다.

위험한 수리는 차이의 증거를 없애는 것이다. 모든 레거시 옵션을 켜고, 인증서 검증을 끄고, 오류를 없애려고 아무 암호나 받아들이면 엔드포인트가 동의하는 범위가 넓어지고, 약한 결과를 보고했을 검사가 사라진다.

오래된 옵션 하나를 유지해야 한다면 설정 주석에 이유와 클라이언트를 적고, 그 옵션을 필요한 프로필로 한정하라. 리스너 하나로 한정된 호환 예외는 감사할 수 있지만, 전역으로 적용한 것은 아무도 선택한 기억이 없는 더 약한 기본값일 뿐이다.

원인을 이름 붙이기 전에 실패를 읽어라

어떤 변경 전에도 정확한 오류 문자열, 클라이언트와 서버 버전, 시각을 보존하라. 그다음 분류하라. 제안에서 불일치했는지, 패킷 경로가 실패했는지, 아니면 한쪽이 예전에 받아들이던 매개변수를 거부했는지.

제안 불일치는 보통 문제 버전이나 알고리즘을 지목하고 매번 같은 지점에서 실패한다. 라우팅이나 MTU 문제는 멈추거나 시간 초과되며 네트워크에 따라 자주 달라진다. 만료된 인증서는 협상이 아니라 검증 오류를 낸다.

어느 것도 공격자의 증거가 아니다. 첫 실패에 악의적 원인을 부여하는 것이 잘못이다. 내일 다시 실패할 결정론적 버그를 가리기 때문이다.

실제로 무엇이 선택되었는지 확인하라

어떤 수정 후에도 연결이 올라왔다고 믿는 대신 협상된 결과를 읽어라. 검증을 제거해서 성공한 연결은 피하려던 결과 그 자체다. 다음 두 명령은 TLS가 앞에 있는 엔드포인트에서 선택된 버전과 암호, 그리고 OpenVPN 서버 로그의 제어 채널 줄을 보여준다.

openssl s_client -connect 198.51.100.30:443 -servername vpn.example.net -brief </dev/null
grep -iE 'Control Channel|Data Channel' /var/log/openvpn.log | tail -5

결과를 지금 연결되는 것과 비교하지 말고 의도한 설정과 비교하라. 선택된 버전이 허용된 최솟값보다 오래되었으면 프로필이나 폴백 목록을 볼 곳이고, 답은 그 목록을 고치는 것이지 공격을 선언하고 주변을 다시 짓는 것이 아니다.

관련 글