Devin.KR

CS · 기본

개발자를 위한 컴퓨터 사이언스 - 내 코드가 실제로 도는 곳

이름과 신뢰의 연결 - DNS 캐시, TLS 핸드셰이크와 인증서 검증 (CS 기초 12장)

DNS 조회와 TTL이 적용되는 캐시 위치를 나누고 TLS에서 이름·기간·인증서 체인을 확인하는 이유를 살펴보며 주소 변경과 인증서 오류의 진단 순서를 익힌다.

개발자 · 원고 갱신

이 장에서 배우는 것

11장에서 연결과 바이트 전달의 약속을 살펴봤다. 이번 장은 어느 주소에 연결할지 찾고 그 상대의 이름을 확인하는 과정을 다룬다. 선수지식은 IP 주소와 TCP 연결의 구분이다.

  • 이름 조회 경로와 캐시 위치를 구분한다.
  • TTL을 받은 시점을 포함해 해석한다.
  • TLS의 암호화와 서버 이름·신뢰 경로 검증을 설명한다.
  • 일부 사용자만 겪는 주소 변경·인증서 오류를 단계별로 진단한다.

개념

도메인의 주소를 바꿨는데 일부 사용자는 새 화면을 보고 일부는 이전 화면을 본다고 가정한다. 새 주소에 직접 접속하면 인증서 오류도 발생한다. 같은 장애처럼 보이지만 이름을 주소로 바꾸는 단계와 상대의 신원을 확인하는 단계가 겹쳐 나타난 상황일 수 있다. 두 단계를 따로 기록하면 주소 변경과 인증서 교체를 동시에 반복하는 실수를 줄일 수 있다.

이름 해석의 결과는 여러 위치에 남는다

애플리케이션은 보통 운영체제의 이름 해석 기능을 통해 주소를 얻는다. 실제 해석에는 로컬 설정과 캐시가 관여할 수 있고 DNS 질의는 재귀 해석기가 대신 처리할 수 있다. 필요한 정보가 없으면 해석기는 위임 관계를 따라 권한 있는 서버의 정보를 찾는다. 매 요청마다 클라이언트가 루트부터 모든 서버에 직접 질문한다고 이해하면 캐시와 관측 위치를 놓친다.

DNS 레코드의 TTL은 캐시에 보관할 수 있는 시간을 표현한다. 권한 있는 서버에서 값을 바꾸어도 이미 다른 곳에 저장된 응답이 즉시 사라지는 것은 아니다. 변경 직전에 TTL을 낮춰도 이전의 긴 TTL로 받은 캐시는 그 사실을 아직 모를 수 있다. 이름 변경을 준비할 때는 새 값의 정확성과 기존 응답이 남는 시간을 별도로 생각해야 한다.

이름 해석의 논리 경로
---------------------
애플리케이션·로컬 설정·캐시
  → 재귀 해석기의 응답 또는 캐시
  → 필요할 때 위임 경로와 권한 있는 서버
응답을 받은 시점과 남은 TTL은 위치마다 다르다.

암호화와 상대 이름 확인을 함께 다룬다

TLS 핸드셰이크는 통신에 필요한 암호 매개변수와 키를 합의하고 상대 인증에 필요한 절차를 수행한다. 일반적인 인증서 기반 서버 인증에서는 신뢰할 수 있는 발급 경로와 유효 기간뿐 아니라 접속하려는 이름도 맞는지 확인한다. 암호문으로 통신한다는 사실만으로 원하는 상대에게 연결됐다고 결론 내릴 수 없다. 이름 확인을 끄는 것은 인증서 오류의 해결이 아니다.

인증서 체인은 서버 인증서에서 중간 인증 기관을 거쳐 클라이언트가 신뢰하는 지점으로 이어지는 관계다. 모든 클라이언트가 같은 신뢰 저장소와 시간을 쓰지는 않는다. 서버가 필요한 중간 인증서를 전달하지 않으면 일부 클라이언트만 실패할 수도 있다. 오류 문구를 보존하면 이름 불일치, 시간 문제, 신뢰 경로 문제를 구분할 출발점을 얻는다.

단계확인 질문다음 단계의 성공을 보장하는가
DNS이 위치에서 어떤 주소를 얻었는가TCP 연결 성공은 별도
TCP그 주소의 상대와 연결했는가TLS 인증 성공은 별도
TLS이름·기간·신뢰 경로 검증에 성공했는가애플리케이션 응답 성공은 별도
애플리케이션기대한 요청 결과를 받았는가앞선 캐시의 보편적 상태는 알 수 없음

인증서를 제시할 호스트 이름을 전달하는 기능과 받은 인증서의 이름을 검증하는 기능도 다르다. 여러 사이트를 제공하는 서버는 전달받은 이름에 맞추어 인증서를 고를 수 있다. 검증은 그 뒤 클라이언트가 기대한 이름과 인증서의 관계를 확인하는 일이다. IP 주소로 바꿔 접속하면 원래의 이름 기반 검증 조건까지 바뀌므로 동일한 시험이라고 보기 어렵다.

같은 이름이라도 요청한 레코드 종류와 조회 위치를 기록해야 비교 조건이 맞는다. 서로 다른 시점에 받은 주소 목록을 한 값처럼 비교하면 정상적인 차이를 오류로 볼 수 있다. 이름과 주소 사이의 관측 조건을 보존한다. 인증서 조사에서도 실제 연결 이름을 남겨야 DNS 문제와 이름 검증 문제를 뒤섞지 않는다.

DNS는 이름으로 주소를 찾고, 연결 뒤 TLS는 인증서의 신뢰 경로와 기간 및 기대 이름을 검증한다.

DNS는 이름으로 주소를 찾고, 연결 뒤 TLS는 인증서의 신뢰 경로와 기간 및 기대 이름을 검증한다.

직접 확인하기

TTL은 응답을 받은 시점부터 계산한다

stored_at = 100
ttl = 300
for now in [250, 399, 400]:
    remaining = max(0, stored_at + ttl - now)
    print(now, 'remaining=', remaining,
          'reusable=', remaining > 0)

Python 3로 실행하는 단일 캐시의 학습 모델이다. 숫자는 실제 DNS 조회 시간이나 측정값이 아닌 직접 정한 입력이다. 400에서 남은 시간이 0이 되어 이 모델은 재사용하지 않는다. 실제 해석기는 추가 정책을 가질 수 있으므로 출력으로 인터넷 전체의 전파 완료 시각을 계산할 수 없다.

출력
----
250 remaining= 150 reusable= True
399 remaining= 1 reusable= True
400 remaining= 0 reusable= False

조회 경로에서 이름과 주소의 역할을 분리한다

from urllib.parse import urlsplit
from ipaddress import ip_address
url = urlsplit('https://reader.example:8443/chapter')
address = ip_address('192.0.2.24')
print('expected name:', url.hostname)
print('selected address:', address)
print('request path:', url.path)

실습용 예약 이름과 문서용 주소를 사용하며 실제로 연결하지 않는다. 이름은 인증서 검증의 기대 대상이고 주소는 연결 경로에서 사용하는 값이라는 구분을 위한 예제다. 같은 주소에서 여러 이름을 서비스할 수 있으므로 주소가 맞다는 사실만으로 이름 검증을 생략하지 않는다.

출력
----
expected name: reader.example
selected address: 192.0.2.24
request path: /chapter

검증을 유지한 클라이언트 설정을 관찰한다

import ssl
context = ssl.create_default_context()
print('verify certificate:',
      context.verify_mode == ssl.CERT_REQUIRED)
print('verify hostname:', context.check_hostname)

표준 라이브러리의 기본 클라이언트 문맥을 생성해 인증서 검증과 호스트 이름 검증이 켜져 있는지 확인한다. 이 예제는 네트워크에 접속하지 않으며 실제 인증서 체인이나 TLS 핸드셰이크 성공을 검증하지 않는다. 신뢰 저장소가 비어 있거나 연결 이름이 다르면 설정이 켜져 있어도 실제 연결은 실패할 수 있다.

출력
----
verify certificate: True
verify hostname: True
직접 만든 오류 분류 연습
-----------------------
주소를 못 얻음        이름 해석부터 확인
이름 불일치          기대 이름과 인증서 이름 확인
유효 기간 오류        인증서 기간과 클라이언트 시간 확인
발급 경로 검증 실패   중간 인증서와 신뢰 저장소 확인

오류 분류표는 실제 도구 화면을 옮긴 것이 아니다. 어느 확인 작업부터 시작할지 정하는 자체 체크표다. 동일한 오류라도 서버 배포와 클라이언트 환경 중 어느 쪽에서 바뀌었는지 함께 살펴야 한다. 연결할 때 검증을 끄는 실험은 성공처럼 보이는 값을 만들지만 원인이 고쳐졌다는 증거를 주지 않는다.

환경별 차이

환경이름 해석 차이신뢰 확인 차이
리눅스로컬 해석 설정과 사용 중인 해석기배포판·런타임의 신뢰 저장소
macOS시스템·VPN·앱 설정에 따른 경로시스템과 개별 런타임의 저장소 차이
컨테이너컨테이너의 이름 해석 설정이미지에 포함된 인증서 묶음과 시간

호스트에서 접속되는데 컨테이너에서만 실패하면 두 환경이 같은 DNS 응답과 신뢰 저장소를 쓰는지부터 확인한다. 애플리케이션의 오류를 운영체제의 모든 프로그램에 일반화하지 않는다. 같은 서버를 호출하더라도 실행 파일과 런타임이 선택한 저장소는 다를 수 있다. 조사 기록에는 클라이언트 종류, 버전, 기대 이름, 오류 단계와 시각을 함께 남긴다.

TLS의 세부 왕복 횟수는 버전, 재개 여부, 인증 방식에 영향을 받는다. 이 장은 TLS 1.3 표준의 개념을 참고하지만 모든 접속이 동일한 횟수로 끝난다고 가정하지 않는다. 재사용한 연결에는 새 이름 해석이나 새 핸드셰이크가 관측되지 않을 수 있다. 첫 접속과 두 번째 접속의 시간을 비교할 때는 이 차이를 별도 조건으로 적어야 한다.

실무에서 자주 틀리는 것

1. TTL을 낮춘 순간 옛 주소가 사라진다고 생각한다

증상은 변경 직후 일부 사용자만 이전 서버로 가는 것이다. 흔한 오진은 권한 있는 서버의 수정이 실패했다고 단정하는 것이다. 실제로 이전 응답을 더 긴 TTL로 저장한 캐시나 기존 연결이 남아 있을 수 있다. 확인은 어떤 위치에서 어떤 응답과 남은 시간을 받았는지 기록하는 것이다. 새 응답 한 번을 확인했다고 모든 사용자의 캐시가 갱신됐다고 보고하지 않는다.

2. IP로 연결되면 인증서가 잘못됐다고 단정한다

증상은 도메인으로는 열리는데 IP 주소로는 이름 오류가 나는 것이다. 흔한 오진은 인증서를 다시 발급해야 한다는 것이다. 실제로 접속 이름이 달라졌으므로 인증서가 그 IP 주소까지 보장하지 않는다면 검증 조건이 충족되지 않을 수 있다. 확인은 요청에 사용한 이름과 인증서가 포함하는 식별자를 비교하는 것이다. 이름 기반 서비스를 시험하면서 주소만 바꾸면 시험 대상도 달라진다.

3. 인증서 오류를 전부 만료로 분류한다

증상은 어떤 클라이언트는 성공하고 어떤 클라이언트는 검증에 실패하는 것이다. 흔한 오진은 유효 기간 숫자만 보고 정상이라고 끝내는 것이다. 실제 원인은 이름, 중간 인증서, 신뢰 저장소, 클라이언트 시간 중 하나일 수 있다. 확인은 원래 오류를 보존한 상태에서 항목별로 비교하는 것이다. 검증을 해제한 성공 결과를 해결 확인으로 사용하면 신뢰 검사를 통째로 우회하게 된다.

스스로 확인하기

  1. 시각 100에 TTL 300인 응답을 저장했다. 시각 250의 남은 TTL과 시각 400의 재사용 여부를 실습 모델에 따라 계산하라.
  2. DNS가 새 주소를 응답하면 TLS 검증까지 성공했다고 말할 수 있는가.
  3. 유효 기간이 남았는데 한 컨테이너만 인증서 검증에 실패한다. 다음에 비교할 항목 두 가지를 설명하라.

해설 1. 남은 시간은 150이며 400에서는 0이라 재사용하지 않는다. 실제 관측에서는 캐시 정책과 응답을 받은 위치를 추가로 기록한다.

해설 2. 아니다. DNS 응답, 연결, TLS 인증, 애플리케이션 결과는 각각 별도 단계이며 한 단계의 성공이 뒤의 성공을 대신하지 않는다.

해설 3. 기대 이름과 인증서 식별자, 서버가 보낸 중간 인증서, 컨테이너 신뢰 저장소와 시간 등을 비교할 수 있다. 이름 확인을 끄는 것은 검증이 아니다.

출처·작성 안내: 본문 설명, 예제, 문제와 도식은 이 장을 위해 직접 작성했다. 아래 문서는 기술적 사실 확인에 참고했으며 원문 문장·표·그림·코드를 발췌하거나 번역하지 않았다.

참고: RFC 1034 DNS 개념, RFC 8446 TLS 1.3, Python ssl 문서

마지막 13장에서는 지금까지의 계층을 하나의 요청 시간선에 놓고 어느 구간을 먼저 조사할지 결정한다.

READER FEEDBACK

질문·오탈자·의견

내용에 관한 질문이나 오탈자, 더 나은 설명을 위한 의견을 남겨 주세요. 이 댓글은 원래 게시글과 같은 자리에 쌓입니다.

댓글 0

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

댓글을 남기려면 로그인이 필요합니다.