security

인증서 체인 불완전 및 도메인 불일치 문제: 단계별 문제 해결 가이드

인증서 체인 불완전 및 도메인 불일치 문제를 해결하는 간단한 단계별 가이드를 통해 웹사이트의 안전성을 확보하세요.

2025년 10월 04일
인증서 체인 도메인 불일치 SSL 웹사이트 보안 문제 해결 가이드 인증서 오류
5분 읽기

웹사이트가 “안전하지 않음” 경고를 띄우거나 API 클라이언트가 SSL/TLS 오류로 실패할 때, 가장 흔한 원인은 두 가지입니다. 바로 인증서 체인이 불완전하거나, 인증서의 도메인 정보가 접속하려는 호스트와 불일치하는 문제입니다. 이 가이드는 실제 운영 환경에서 바로 적용할 수 있는 명령어, 서버별 설정 예시, 체크리스트를 통해 두 문제를 빠르게 진단하고 해결하는 방법을 단계별로 안내합니다.

핵심 요약: 왜 이 문제가 중요한가

  • 인증서 체인 불완전: 서버가 중간 인증서를 제대로 제공하지 않아, 클라이언트가 신뢰 경로를 완성하지 못하는 상태.
  • 도메인 불일치: 인증서의 CN 또는 SAN(Subject Alternative Name)에 접속 도메인이 없어 호스트네임 검증에 실패.

두 문제는 브라우저 경고(“이 연결은 완전히 안전하지 않습니다”), curl/openssl 오류(“unable to get local issuer certificate”, “certificate verify failed”, “host name mismatch”)로 드러납니다. 검색 순위 하락, 전환율 감소, API 통신 장애 등 사업적 리스크가 크므로 즉시 조치가 필요합니다.


기본 개념 정리: 인증서 체인과 도메인 일치

인증서 체인(Trust Chain)이란?

  • Leaf(서버) 인증서: 여러분의 도메인에 발급된 인증서.
  • Intermediate(중간) 인증서: 루트 인증서와 서버 인증서를 연결하는 사슬.
  • Root(루트) 인증서: OS/브라우저의 신뢰 저장소에 기본 탑재된 신뢰 anchor.

서버는 일반적으로 “서버 인증서 + 중간 인증서(들)”을 함께 제공해야 하며, 루트 인증서는 제공하지 않습니다(클라이언트가 자체 보유).

도메인 일치(Hostname Validation)

  • 인증서의 CN 또는 SAN 목록에 접속하는 호스트명이 반드시 포함되어야 합니다.
  • 와일드카드 예: *.example.com은 www.example.com, api.example.com은 커버하지만 example.com은 커버하지 않습니다.
  • IP 주소 접속 시: 인증서의 SAN에 해당 IP가 표기돼 있어야 합니다. 대부분의 공인 인증서는 IP SAN을 포함하지 않습니다.

빠른 진단 체크리스트

다음 중 하나라도 해당한다면, 아래 단계별 해결을 진행하세요.

  • 브라우저: ERR_CERT_AUTHORITY_INVALID, ERR_CERT_COMMON_NAME_INVALID 표시
  • curl: SSL certificate problem: unable to get local issuer certificate / certificate verify failed
  • openssl: verify error:num=20: unable to get local issuer certificate, or hostname mismatch
  • 특정 OS/브라우저/모바일에서만 오류 발생(중간 인증서 혹은 체인 선택 문제 가능성 높음)
  • www 유입은 정상이나 apex(루트 도메인) 또는 특정 서브도메인만 실패(도메인 불일치 가능성)

1부. 인증서 체인 불완전 문제: 증상, 원인, 해결

흔한 오류 메시지

  • 브라우저: ERR_CERT_AUTHORITY_INVALID, “인증서가 신뢰되지 않습니다”
  • curl:
    • SSL certificate problem: unable to get local issuer certificate
    • error:0A00018E:SSL routines::ca md too weak (환경에 따라)
  • openssl verify: error 20 at 0 depth lookup: unable to get local issuer certificate

1단계: 현재 체인 상태 확인

다음 명령으로 서버가 제공하는 인증서와 체인을 확인합니다.

echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | openssl x509 -noout -subject -issuer -dates
  • 여러 개의 인증서 블록이 출력됩니다. 순서가 Leaf → Intermediate(들)인지 확인하세요.
  • issuer가 다음 인증서의 subject와 매칭되는지 확인합니다.

또는 SAN과 issuer 확인:

echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | openssl x509 -noout -subject -issuer -ext subjectAltName

SSLLabs 서버 테스트를 활용하면 체인 문제, 호환성, TLS 설정을 한 번에 확인할 수 있습니다.

2단계: 올바른 중간 인증서 확보

문제의 대부분은 “중간 인증서 파일 누락” 혹은 “잘못된 중간/체인 선택”에서 발생합니다. 해결을 위해 다음을 수행하세요.

  • 인증서를 발급한 CA의 공식 문서에서 해당 서버 인증서에 맞는 최신 중간 인증서를 다운로드합니다.
  • Let’s Encrypt를 사용한다면 보통 fullchain.pem이 서버 인증서 + 중간 인증서를 포함합니다.
  • 오래된 체인이나 교차서명(cross-signed) 체인을 잘못 선택하면 특정 기기(특히 구형 Android)에서만 실패할 수 있습니다. CA에서 권장하는 최신 체인을 사용하세요.

3단계: 서버별 올바른 체인 설치

서버/로드밸런서/프록시별로 체인을 지정하는 방식이 다릅니다.

  • Nginx

    • ssl_certificate: 서버 인증서 + 중간 인증서가 포함된 fullchain.pem 지정

    • ssl_certificate_key: 프라이빗 키

    • 예:

      server {
        listen 443 ssl http2;
        server_name example.com www.example.com;
      
        ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
      
        # (선택) OCSP Stapling 안정성을 위해
        ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
      }
      
  • Apache (2.4.8 이상)

    • SSLCertificateFile에 fullchain.pem 지정, SSLCertificateKeyFile에 키 지정.

    • 예:

      <VirtualHost *:443>
        ServerName example.com
        SSLEngine on
        SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
      </VirtualHost>
      
    • Apache 2.4.7 이하에서는 SSLCertificateChainFile을 별도로 사용합니다.

  • HAProxy

    • 하나의 PEM 파일에 private key + fullchain을 순서대로 결합:

      cat /etc/letsencrypt/live/example.com/privkey.pem \
          /etc/letsencrypt/live/example.com/fullchain.pem \
          > /etc/haproxy/certs/example.com.pem
      
    • frontend에 crt 또는 crt-list로 지정.

  • IIS (Windows)

    • PFX로 가져오기: 프라이빗 키와 체인을 포함한 PFX를 만듭니다.

      openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out example.pfx -name "example.com"
      
    • “컴퓨터 계정 > 개인” 스토어로 가져온 뒤 사이트 바인딩에서 해당 인증서 선택. SNI 사용 시 도메인별 바인딩 필요.

  • AWS ALB/ELB/CloudFront

    • AWS Certificate Manager(ACM)에 “인증서 + 체인 + 키”를 업로드(가져오기)하거나 ACM에서 발급받습니다.
    • ALB 리스너에 해당 인증서를 연결합니다.
    • CloudFront는 us-east-1(버지니아 북부) 리전에 있는 ACM 인증서만 선택 가능하며, 배포 Alternate Domain Names(CNAME)에 도메인이 모두 포함되어야 합니다.
  • Java/Tomcat

    • PKCS#12를 생성 후 Keystore로 등록:

      openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out example.p12 -name example
      keytool -importkeystore -destkeystore keystore.jks -srckeystore example.p12 -srcstoretype PKCS12 -alias example
      
    • server.xml에서 keystore 지정을 확인합니다.

중요: 반드시 “서버 인증서 → 중간 인증서(들)” 순서로 연결되어야 하며, 루트 인증서는 포함하지 않습니다.

4단계: 재기동 및 검증

  • 서비스를 재시작한 후 다음 명령으로 재확인:

    curl -vI https://example.com
    echo | openssl s_client -connect example.com:443 -servername example.com -showcerts
    
  • SSLLabs 테스트에서 체인 문제 경고가 사라졌는지 확인합니다.

5단계: 까다로운 사례와 회피 전략

  • 교차서명 문제: 과거 Let’s Encrypt의 DST Root CA X3 만료처럼, 구형 클라이언트 호환을 고려해야 할 때가 있습니다. CA에서 권장하는 최신 체인으로 전환하세요.
  • 특정 기기에서만 실패: 해당 기기의 신뢰 저장소에 루트가 없거나, 서버가 구식 체인을 제공하는 경우가 많습니다. 체인을 최신화하세요.
  • 프록시/중간 장비: TLS 오프로드를 수행하는 장비(로드밸런서, CDN, IDS 등)에도 동일하게 올바른 체인을 설정해야 합니다.

2부. 도메인 불일치 문제: 증상, 원인, 해결

흔한 오류 메시지

  • 브라우저: ERR_CERT_COMMON_NAME_INVALID, “호스트명이 인증서와 일치하지 않습니다”
  • curl: SSL: no alternative certificate subject name matches target host name 'api.example.com'
  • Java: javax.net.ssl.SSLPeerUnverifiedException: Hostname verification failed

1단계: 접속 호스트와 인증서 SAN 비교

다음 명령으로 인증서의 SAN 목록을 확인합니다.

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
  • SAN에 접속하려는 호스트명이 없는지 확인합니다.
  • 와일드카드 *.example.com은 example.com(루트 도메인)을 커버하지 않습니다. 필요한 모든 호스트를 SAN에 추가해야 합니다.

2단계: SNI(Server Name Indication)와 라우팅 확인

같은 IP/포트에 여러 도메인을 호스팅할 때는 SNI가 필수입니다. 다음으로 점검하세요.

  • 테스트 시 -servername 옵션을 반드시 지정:

    echo | openssl s_client -connect 203.0.113.10:443 -servername www.example.com
    
  • curl로 특정 IP에 도메인을 강제 맵핑해 실제 라우팅을 재현:

    curl -vI --resolve www.example.com:443:203.0.113.10 https://www.example.com
    

위 테스트에서 반환되는 인증서의 CN/SAN이 기대와 다르면, 가상호스트 설정 또는 로드밸런서의 도메인-인증서 매핑이 잘못되었을 가능성이 큽니다.

3단계: 서버/프록시별 도메인 매핑 수정

  • Nginx

    • server_name으로 정확한 도메인을 지정하고, 각 서버 블록에 올바른 인증서를 매핑:

      server {
        listen 443 ssl http2;
        server_name api.example.com;
      
        ssl_certificate     /etc/ssl/api.example.com/fullchain.pem;
        ssl_certificate_key /etc/ssl/api.example.com/privkey.pem;
      }
      
      server {
        listen 443 ssl http2;
        server_name www.example.com;
      
        ssl_certificate     /etc/ssl/www.example.com/fullchain.pem;
        ssl_certificate_key /etc/ssl/www.example.com/privkey.pem;
      }
      
    • default_server에 잘못된 인증서가 걸려 있으면 SNI 미지원 클라이언트가 틀린 인증서를 받을 수 있습니다. 기본 서버는 444 반환 등으로 안전하게 처리하세요.

  • Apache

    • 도메인별 VirtualHost에 각각 올바른 SSLCertificateFile/KeyFile 설정.
    • ServerName/ServerAlias가 실제 DNS와 일치하는지 점검.
  • HAProxy

    • crt-list로 도메인별 인증서 지정 또는 SNI 기반 라우팅:

      frontend https-in
        bind :443 ssl crt-list /etc/haproxy/crt-list.txt
      
  • AWS ELB/ALB

    • 리스너에 연결된 인증서가 해당 도메인을 커버하는지 확인.
    • 다수의 도메인을 같은 리스너에서 처리할 경우 SNI 인증서(멀티 도메인 또는 와일드카드)를 연결.
  • CloudFront

    • Alternate Domain Names(CNAME)에 도메인 추가, 해당 도메인을 커버하는 ACM 인증서 선택.
    • DNS CNAME 레코드가 CloudFront 도메인을 가리키는지 점검.

4단계: 인증서 재발급 또는 SAN 확장

  • 필요한 모든 호스트명을 SAN에 포함해 재발급하세요.

  • Let’s Encrypt 예시:

    sudo certbot certonly --nginx -d example.com -d www.example.com -d api.example.com
    
  • 와일드카드가 유효한 경우:

    sudo certbot certonly --dns-cloudflare -d "*.example.com" -d example.com
    

주의: 와일드카드는 서브도메인만 커버하며 루트 도메인은 별도로 포함해야 합니다.

5단계: 특수 사례

  • IP 주소로 접속: 인증서 SAN에 IP를 추가하지 않았다면 호스트 검증이 실패합니다. FQDN 기반 접속으로 전환하거나, 내부망이라면 사설 CA로 FQDN 인증서를 발급하세요.
  • IDN(국제화 도메인): 인증서에는 퓨니코드(xn-- 형태)로 SAN이 기록됩니다. 발급 시 퓨니코드 변환이 올바른지 확인하세요.
  • 역프록시/백엔드 연동: 프록시가 Host 헤더를 보존하지 않으면 백엔드의 SNI/가상호스트 라우팅이 꼬일 수 있습니다. 예: Nginx에서 proxy_set_header Host $host; 설정 유지.

실전 예시 1: Let’s Encrypt + Nginx 체인 불완전 문제 해결

증상: 모바일 브라우저에서만 “인증서를 확인할 수 없습니다” 경고 발생.

해결:

  1. 확인
echo | openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts

중간 인증서가 누락되어 있음을 확인.

  1. fullchain.pem 사용하도록 Nginx 설정 수정
ssl_certificate     /etc/letsencrypt/live/www.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
  1. 재시작 후 SSLLabs로 재검증. 모든 클라이언트에서 신뢰됨을 확인.

실전 예시 2: 멀티 도메인 사이트의 도메인 불일치 해결(AWS ALB)

증상: api.example.com은 정상이나 admin.example.com은 ERR_CERT_COMMON_NAME_INVALID.

원인: ALB 리스너에 연결된 인증서가 api.example.com만 포함.

해결:

  1. ACM에서 멀티 도메인 인증서 발급(SAN에 api.example.com, admin.example.com 모두 포함).
  2. ALB 리스너 인증서를 새 인증서로 교체.
  3. curl --resolve로 IP 강제 후 검사:
curl -vI --resolve admin.example.com:443:ALB_IP https://admin.example.com

SAN 포함 및 정상 응답을 확인.


실전 예시 3: 사내 내부망(IP 접속) API의 호스트 검증 실패

증상: 내부 API를 https://10.0.0.5로 호출 시 Java 클라이언트에서 SSLPeerUnverifiedException.

해결 방향:

  • FQDN 내부 도메인(api.internal.local)을 사설 DNS에 등록하고, 사설 CA로 해당 FQDN에 대한 인증서 발급.
  • 클라이언트는 사설 CA 루트를 신뢰 저장소에 추가.
  • IP 직접 접속 대신 FQDN 접속으로 전환.

장애 재현과 원인 추적에 유용한 명령어 모음

  • 서버 인증서/체인 확인
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts
  • SAN, subject, issuer, 만료일 확인
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer -dates -ext subjectAltName
  • 체인 검증
openssl verify -CAfile chain.pem server-cert.pem
  • 특정 IP로 도메인 연결하여 테스트
curl -vI --resolve www.example.com:443:203.0.113.10 https://www.example.com
  • 엄격 검증으로 요청(로컬 CA 번들 지정)
curl --cacert /etc/ssl/certs/ca-bundle.crt https://example.com -v

운영 환경별 체크리스트

  • 웹서버/프록시/로드밸런서 모든 경로에 동일하게 올바른 체인을 배치했는가?
  • 서버 인증서 파일이 fullchain(서버+중간)인지 확인했는가?
  • 루트 인증서를 의도치 않게 함께 제공하지 않는가?
  • SAN에 필요한 모든 호스트명이 포함되어 있는가(루트, www, api 등)?
  • 와일드카드 사용 시 루트 도메인을 별도로 포함했는가?
  • SNI 매핑이 올바른가(가상호스트, crt-list, ALB 리스너 설정)?
  • 역프록시가 Host 헤더를 보존하는가?
  • 만료일을 모니터링하고 자동 갱신(ACME, Certbot 등)이 설정되어 있는가?

예방 전략과 베스트 프랙티스

  • 자동화

    • ACME 기반 자동 갱신(Certbot, lego, acme.sh)을 도입하고, 갱신 후 웹서버 재로드를 자동화하세요.
    • CI/CD 파이프라인에서 배포 전 openssl/curl 검증 스텝을 추가합니다.
  • 모니터링

    • 인증서 만료 모니터링(예: Prometheus exporter, Cron + 메일 알림, 사내 대시보드).
    • SSLLabs, Hardenize, Censys 등을 통한 정기 점검.
    • Certificate Transparency(CT) 로그 모니터링으로 의도치 않은 발급 탐지.
  • 호환성과 보안

    • 최신 중간 인증서를 사용하고, 구형 클라이언트 호환이 필요한 경우 CA 권고 체인을 따릅니다.
    • 강력한 암호 스위트와 최신 프로토콜(TLS 1.2/1.3)을 우선 적용.
    • HSTS를 신중히 설정하여 중간자 공격 위험을 감소(도메인 일치 및 체인 문제가 모두 해결된 뒤 적용).

자주 묻는 질문(FAQ)

  • 루트 인증서를 서버가 같이 제공해야 하나요?

    • 아니요. 클라이언트가 자체 신뢰 저장소에 보유하므로, 일반적으로 루트는 제공하지 않습니다.
  • 체인 순서가 중요합니까?

    • 네. 서버 인증서 → 중간 인증서(들) 순서가 중요합니다.
  • 와일드카드 하나면 모두 해결되나요?

    • 아닙니다. *.example.com은 example.com(루트)을 커버하지 않으며, 하위 하위 도메인(a.b.example.com)도 커버하지 않습니다.
  • CloudFront에서 인증서가 있는데도 도메인 불일치가 납니다.

    • CloudFront 배포의 Alternate Domain Names(CNAME)에 해당 도메인이 포함되었는지, 그리고 us-east-1 리전의 ACM 인증서를 선택했는지 확인하세요.

문제 해결의 결정 트리(요약)

  1. 오류 메시지 확인
  • authority invalid → 체인 불완전 가능성
  • common name invalid / hostname mismatch → 도메인 불일치 가능성
  1. s_client와 curl로 재현
  • -servername, --resolve를 활용해 정확히 어떤 인증서가 제공되는지 확인
  1. 체인 문제면
  • CA 권장 중간 인증서로 fullchain 구성
  • 서버/프록시/로드밸런서별로 올바르게 배치
  • SSLLabs로 검증
  1. 도메인 불일치면
  • SAN에 필요한 호스트 모두 포함해 재발급
  • SNI 기반 매핑/가상호스트 설정 수정
  • CDN/ALB의 인증서 연결 및 CNAME 설정 점검

마무리

인증서 체인 불완전과 도메인 불일치는 증상은 비슷해도 해결 경로가 다릅니다. 핵심은 “정확한 재현”과 “올바른 파일/설정 배치”입니다. 이 가이드의 진단 명령과 서버별 설정 예시, 체크리스트를 따라가면 대부분의 문제는 수십 분 내에 해결할 수 있습니다. 이후에는 자동화와 모니터링을 통해 같은 문제가 반복되지 않도록 예방하세요.

안정적으로 신뢰할 수 있는 HTTPS는 사용자 신뢰와 전환율, 검색 노출까지 연결됩니다. 지금 바로 확인하고 정리해 보세요.

개발 파트너가 필요하신가요?

DevTeam은 MVP·웹·앱·AI 개발을 설계부터 배포·운영까지 한 팀이 책임집니다.

이 글 공유하기
Twitter LinkedIn
최종 수정: 2025년 10월 04일

security 관련 글

더 많은 스타트업 노하우와 비즈니스 인사이트를 확인해보세요

TLS 제대로 설정하기: TLS 1.2 이상만 허용하고 SSL Labs A+ 받...

자물쇠 아이콘 뒤를 단단하게 — 낡은 프로토콜 차단, 강한 암호 스위트, 인증서 자동...

Zero Trust로 사는 법: 나 자신조차 믿지 않는 보안 설계와 권한...

분실·실수·피싱이라는 평범한 사고가 재앙이 되지 않게 만드는 권한 분산과 다채널 검...

사람과 물리, 기술 너머의 공격면: 피싱·딥페이크·내부자·물리보...

방어는 가장 약한 고리만큼만 강하다 — 소셜 엔지니어링·내부자 위협·물리적 접근·서...

복잡한 비밀번호의 환상: 90일 변경·특수문자 미신부터 MFA·IP...

비밀번호를 완벽하게 만드는 대신, 비밀번호 하나가 뚫려도 무너지지 않는 구조를 만...

침해를 가정하라: 로깅·탐지·사고대응부터 랜섬웨어 막는 3-2-1...

완벽한 예방은 없다 — 체류 시간을 줄이는 탐지부터 랜섬웨어를 무력화하는 불변 백업...

AI가 바꾼 공격의 경제학: 정밀 공격의 대량 생산 시대, 방어자...

공격 단가가 0에 수렴하는 시대, n-day와 AI 피싱에 맞서는 실전 방어 전략

전문가 도움이 필요하신가요?

스타트업과 비즈니스 성장을 위한 전문 컨설팅을 받아보세요.
확장 가능하고 비즈니스 성과로 이어지는 솔루션을 구축할 수 있도록 도와드립니다.