Develope/Node.js

Node.js에서 TLS 인증서 검증은 어떻게 동작하는가

oper0116 2026. 4. 14. 20:50
반응형

1. 개요

Node.js에서 HTTPS 요청을 보내다 보면
다음과 같은 TLS 관련 오류를 만날 때가 있다.

  • UNABLE_TO_VERIFY_LEAF_SIGNATURE
  • SELF_SIGNED_CERT_IN_CHAIN
  • CERT_HAS_EXPIRED
  • ERR_TLS_CERT_ALTNAME_INVALID

이런 오류를 해결하려면
단순히 "인증서가 이상하다" 정도로 이해하는 것만으로는 부족하다.

실제로는 Node.js
어떤 기준으로 서버 인증서를 검증하는지 이해해야 한다.

이 글에서는 Node.js에서 TLS 인증서 검증이 어떻게 동작하는지 정리한다.


2. TLS 인증서 검증이 필요한 이유

HTTPS는 단순히 암호화만 하는 것이 아니다.

중요한 것은 다음 두 가지다.

  • 지금 연결한 서버가 정말 의도한 서버인지
  • 서버가 제시한 인증서를 신뢰할 수 있는지

즉, TLS 검증은
암호화 이전에 신뢰할 수 있는 상대인지 확인하는 과정이라고 볼 수 있다.

이 검증이 없다면
중간자 공격(MITM) 같은 문제에 취약해질 수 있다.


3. Node.js는 무엇을 검증하는가

Node.js는 HTTPS 연결 시
서버가 보낸 인증서에 대해 대략 다음 항목을 확인한다.

  1. 인증서가 신뢰할 수 있는 CA를 기준으로 발급되었는가
  2. 인증서 체인(chain)이 올바르게 이어지는가
  3. 인증서의 유효 기간이 지나지 않았는가
  4. 접속한 도메인과 인증서의 호스트명이 일치하는가
  5. 인증서가 폐기되었거나 정책상 거부 대상은 아닌가

즉, "인증서가 있다"는 것만으로는 충분하지 않다.
신뢰 가능한 방식으로 발급되었고, 현재 접속 대상과도 일치해야 한다.


4. 신뢰의 시작점: Root CA

TLS 검증은 결국 "누구를 신뢰할 것인가"에서 시작한다.

Node.js는 기본적으로 신뢰할 수 있는 Root CA 목록을 가지고 있다.
이 목록에 포함된 기관이 서명한 인증서 체인만 신뢰할 수 있다.

예를 들어 서버 인증서가 직접 Root CA로부터 발급되는 경우는 드물고,
보통은 다음 구조를 가진다.

Root CA
  └ Intermediate CA
      └ Server Certificate

즉, 서버는 자신의 인증서만 보내는 것이 아니라
중간 인증서까지 함께 보내고,
클라이언트는 이 체인을 따라가며 신뢰 여부를 확인한다.


5. 인증서 체인은 어떻게 검증되는가

Node.js는 서버가 보낸 인증서를 보고
다음처럼 체인을 따라 올라간다.

  1. 서버 인증서의 서명자를 확인한다
  2. 그 서명자가 Intermediate CA인지 확인한다
  3. Intermediate CA의 서명자를 다시 확인한다
  4. 최종적으로 신뢰 가능한 Root CA까지 이어지는지 확인한다

이 과정 중 하나라도 끊기면 검증은 실패한다.

예를 들어:

  • Intermediate 인증서가 누락된 경우
  • 신뢰하지 않는 CA가 서명한 경우
  • 체인이 잘못 연결된 경우

이런 상황에서는 인증서 오류가 발생할 수 있다.


6. 유효 기간도 함께 확인한다

인증서에는 유효 시작 시점과 만료 시점이 있다.

즉, 아무리 신뢰할 수 있는 CA가 발급했더라도
인증서가 만료되었다면 정상적인 인증서로 볼 수 없다.

이 경우 대표적으로 다음과 같은 문제가 발생할 수 있다.

  • CERT_HAS_EXPIRED

실무에서는 인증서 자체보다
단순한 만료 문제로 TLS 연결이 실패하는 경우도 꽤 많다.


7. 호스트명 검증은 왜 중요한가

인증서가 신뢰 가능한 CA에서 발급되었다고 해도
현재 접속한 도메인과 인증서 대상이 다르면 안 된다.

예를 들어 api.example.com에 접속했는데
인증서에는 admin.example.com만 포함되어 있다면
검증은 실패해야 한다.

이 검증은 보통 인증서의 다음 필드를 기준으로 이루어진다.

  • Subject Alternative Name(SAN)

과거에는 Common Name(CN)도 많이 언급됐지만,
현재는 일반적으로 SAN이 더 중요하다.

이 검증이 실패하면 다음과 같은 오류를 만날 수 있다.

  • ERR_TLS_CERT_ALTNAME_INVALID

즉, 인증서가 "정상 발급"된 것과
"현재 접속 대상과 일치"하는 것은 별개의 문제다.


8. Node.js에서 실제 검증은 어디서 일어나는가

Node.js는 내부적으로 OpenSSL을 기반으로 TLS 처리를 수행한다.

즉, HTTPS 요청을 보낼 때
Node.js 애플리케이션 코드가 직접 인증서를 하나씩 검증하는 것이 아니라,
내부 TLS 계층과 OpenSSL이 검증 로직을 수행한다.

개발자는 보통 다음 모듈을 통해 이 동작을 간접적으로 사용한다.

  • https
  • tls
  • http2

예를 들어 https.get()을 호출하면
연결 과정에서 TLS 핸드셰이크와 인증서 검증이 함께 진행된다.

const https = require("https");

https.get("https://example.com", (res) => {
  console.log(res.statusCode);
});

위 코드는 단순해 보이지만,
내부적으로는 인증서 신뢰 체인과 호스트명 검증이 함께 수행된다.


9. self-signed 인증서는 왜 자주 실패하는가

로컬 개발 환경이나 사설망에서는
self-signed certificate를 사용하는 경우가 있다.

문제는 이 인증서가 기본 신뢰 목록에 없다는 점이다.

즉, self-signed 인증서는
인증서 형식이 틀린 것이 아니라
신뢰의 출발점으로 인정되지 않기 때문에 실패한다.

그래서 다음과 같은 오류가 발생할 수 있다.

  • DEPTH_ZERO_SELF_SIGNED_CERT
  • SELF_SIGNED_CERT_IN_CHAIN

이 경우 해결 방법은 보통 다음 중 하나다.

  • 개발용 CA를 신뢰 목록에 추가
  • 사설 CA를 별도로 지정
  • 로컬 개발에서만 제한적으로 예외 처리

다만 검증을 끄는 방식은 편하지만 위험하다.


10. rejectUnauthorized: false는 왜 위험한가

테스트 과정에서 아래 옵션을 사용하는 경우가 있다.

const agent = new https.Agent({
  rejectUnauthorized: false,
});

이 옵션은 서버 인증서 검증을 사실상 무시하게 만든다.

즉, 연결은 될 수 있지만
상대 서버가 진짜인지 보장할 수 없다.

개발 중 임시 확인 용도로는 볼 수 있지만,
운영 환경에서는 매우 위험하다.

👉 TLS 오류를 해결하는 방법은
검증을 끄는 것이 아니라
왜 검증에 실패했는지 원인을 확인하는 것이다.


11. 실무에서 자주 만나는 실패 원인

Node.js에서 TLS 인증서 검증이 실패하는 대표적인 원인은 다음과 같다.

  • Intermediate 인증서 누락
  • 인증서 만료
  • 도메인 불일치
  • self-signed 인증서 사용
  • 사내 프록시 인증서를 신뢰하지 않는 경우
  • 운영체제/런타임의 신뢰 저장소 차이

특히 로컬에서는 되는데
운영 서버에서만 실패하는 경우,
런타임이 신뢰하는 CA 목록 차이를 의심해볼 수 있다.


12. 디버깅할 때 확인할 것

TLS 오류를 만났다면 다음 순서로 확인하면 좋다.

  1. 인증서가 만료되지 않았는가
  2. 접속 도메인과 SAN이 일치하는가
  3. Intermediate 인증서가 빠지지 않았는가
  4. 해당 CA가 Node.js에서 신뢰되는가
  5. 사내 프록시나 보안 장비가 인증서를 바꾸고 있지는 않은가

즉, "인증서가 있다"가 아니라
"신뢰 체인이 완성되는가"를 보는 것이 중요하다.


13. 정리

  • Node.js는 HTTPS 연결 시 서버 인증서를 검증한다
  • 검증 항목에는 CA 신뢰 체인, 유효 기간, 호스트명 일치 여부가 포함된다
  • 내부적으로는 OpenSSL 기반 TLS 계층이 이 검증을 수행한다
  • self-signed 인증서는 기본적으로 신뢰되지 않는다
  • rejectUnauthorized: false는 문제 해결이 아니라 검증 우회다

결국 TLS 인증서 검증의 핵심은
"암호화가 되는가"보다
"지금 연결한 서버를 신뢰할 수 있는가"다.


14. 결론

Node.js에서 TLS 인증서 검증은
단순히 인증서 한 장을 보는 과정이 아니다.

신뢰 가능한 CA를 기준으로
인증서 체인을 따라가고,
유효 기간을 확인하고,
현재 접속한 도메인과도 일치하는지 확인하는 과정이다.

이 흐름을 이해하면
HTTPS 요청 중 발생하는 TLS 오류를 훨씬 정확하게 해석할 수 있다.

특히 실무에서는
오류 메시지만 보고 우회하기보다,
검증이 어떤 단계에서 실패했는지 파악하는 것이 더 중요하다.


반응형