🔐 부적절한 인증서 유효성 검증 (MITM) KISA A-2 · 부적절한 인증서 유효성 검증

TLS 클라이언트가 api.bank.example에 접속합니다. 인증서 검증을 생략하면(trust-all TrustManager, hostname 검증 비활성, verify=False) 중간자가 가짜 인증서를 제시해 TLS를 가로채고 "암호화된" 트래픽을 평문으로 읽을 수 있습니다.

0 · 서버(중간자)가 제시하는 인증서

① 취약한 클라이언트: 전체 신뢰 / 호스트 검증 없음 VULNERABLE

이 클라이언트는 모든 체인을 수용하는 TrustManager를 설치하고 hostname 검증을 끕니다. 어떤 인증서로도 — 중간자의 위조 인증서까지 — 핸드셰이크를 완료합니다.
📱
앱
──▶
🕵️ MITM
──▶
🏦 api.bank

② 안전한 클라이언트: 완전 검증 + 핀닝 SECURE

안전한 클라이언트는 신뢰 CA까지 체인을 검증하고, 만료를 확인하며, hostname이 SAN과 일치하는지 검사하고, (선택적으로) 기대 공개키를 핀닝합니다. 결함이 있으면 구체적 오류로 핸드셰이크를 중단합니다.

③ 취약 vs 안전 코드

VULNERABLE
// Java — 모든 인증서 신뢰 (재앙)
TrustManager[] trustAll = { new X509TrustManager(){
  public void checkServerTrusted(X509Certificate[] c, String a){}
  public void checkClientTrusted(X509Certificate[] c, String a){}
  public X509Certificate[] getAcceptedIssuers(){return null;}
}};
ctx.init(null, trustAll, new SecureRandom());
conn.setHostnameVerifier((host, sess) -> true); // 호스트 검증 무력화

# Python — 검증 끄기
requests.get(url, verify=False)   # 만료/자가서명/불일치 모두 통과
SECURE
// Java — 기본 검증 + 호스트명 + 핀닝
// 기본 TrustManager(시스템 CA) 사용, override 금지
SSLContext ctx = SSLContext.getDefault();
HttpsURLConnection.setDefaultHostnameVerifier(
    HttpsURLConnection.getDefaultHostnameVerifier());
// OkHttp 공개키 핀닝
CertificatePinner pinner = new CertificatePinner.Builder()
  .add("api.bank.example", "sha256/AAAA...expectedKey")
  .build();

# Python — 기본 검증(verify 생략 = True)
requests.get(url)                  # CA체인+만료+호스트 검증
방어 핵심: 운영에서 검증 비활성 금지 · 체인 → 만료 → hostname(SAN) 순서로 검증 · 시스템 신뢰 저장소 사용 · 고가치 API는 서버 공개키 핀닝 · 조용히 진행하지 말고 명확한 오류로 fail-closed.
⚠️ 교육용 시뮬레이션입니다. TLS 핸드셰이크·인증서·MITM은 페이지 내부 mock이며 실제 네트워크 연결이나 암호 연산은 수행하지 않습니다.