메뉴 건너뛰기

app

신뢰의 연결고리를 확인하라: CWE-295 부적절한 인증서 유효성 검증

suritam92026.01.17 22:30조회 수 2댓글 0

    • 글자 크기

1. 부적절한 인증서 유효성 검증의 위험성

CWE-295는 HTTPS 통신 과정에서 서버가 제시한 SSL/TLS 인증서가 신뢰할 수 있는 기관(CA)에서 발급되었는지, 만료되지는 않았는지, 호스트 이름이 일치하는지 등을 제대로 검사하지 않을 때 발생합니다. 공격자는 이를 악용하여 가짜 인증서를 사용한 **중간자 공격(MitM, Man-in-the-Middle)**을 수행할 수 있습니다. 검증 로직이 무력화되면 공격자는 암호화된 통신 내용을 가로채거나(스니핑), 데이터를 변조하여 서버로 전달할 수 있는 치명적인 위협에 노출됩니다.

2. 흔히 발생하는 취약한 패턴: "모두 허용"의 함정

개발 단계에서 사설 인증서를 사용하거나 테스트 편의를 위해 검증 로직을 무력화한 코드가 운영 환경까지 남게 되는 경우가 많습니다.

TrustManager 무력화: checkServerTrusted 메서드 내부를 비워두어 모든 인증서를 신뢰하게 만드는 경우.

HostnameVerifier 무력화: 인증서의 도메인과 실제 접속 도메인이 달라도 무조건 true를 반환하게 설정하는 경우.

만료 및 취소 여부 미확인: 유효기간이 지났거나 도난 등의 이유로 취소된 인증서를 정상으로 판단하는 경우.

3. 실무적 대응: 표준 검증 절차 준수와 핀닝(Pinning)

안전한 통신을 위해 시스템 기본 검증 메커니즘을 사용하고, 필요한 경우 추가 보안을 적용해야 합니다.

기본 검증 메커니즘 유지: 커스텀 TrustManager를 직접 구현하기보다는 Java 시스템이 기본으로 제공하는 신뢰할 수 있는 인증서 저장소(Truststore)를 활용합니다.

정확한 호스트 이름 검증: HttpsURLConnection 사용 시 기본 제공되는 HostnameVerifier를 사용하여 도메인 일치 여부를 반드시 확인합니다.

인증서 핀닝(Certificate Pinning): 특히 모바일 앱의 경우, 특정 서버의 인증서 정보를 앱 내에 미리 저장해두고 통신 시 대조하여 MitM 공격을 원천 차단합니다.

4. CWE-295 대응 및 안전한 TLS 연결 자바 코드 예시

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.net.URL;
import java.security.KeyStore;
 
public class TlsSecurityManager {
    private static final Logger logger = LoggerFactory.getLogger(TlsSecurityManager.class);
 
    // [CWE-295 조치] 시스템 표준 신뢰 저장소를 사용하여 인증서를 올바르게 검증
    public void secureConnect(String urlString) {
        try {
            URL url = new URL(urlString);
            
            // 1. 시스템 기본 TrustManager 사용 (모두 허용하는 커스텀 로직 금지)
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init((KeyStore) null); // 기본 시스템 Truststore 로드
 
            SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
            sslContext.init(null, tmf.getTrustManagers(), new java.security.SecureRandom());
 
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(sslContext.getSocketFactory());
 
            // 2. 호스트 이름 검증 (기본값 사용 권장, 임의로 true 반환 금지)
            // conn.setHostnameVerifier((hostname, session) -> true); // [위험] 절대 금지
 
            conn.connect();
            logger.debug("Successfully established secure connection to: {}", urlString);
 
        } catch (Exception e) {
            // [CWE-754, CWE-390] 인증서 오류 등 예외 상황 대응
            logger.debug("TLS Connection failed: Potential Certificate Issue or MitM attempt: {}", e.getMessage());
        }
    }
}

코멘트: 시큐어 코딩에서 "편의성"과 "보안"은 종종 충돌합니다. 테스트를 위해 넣었던 checkServerTrusted 내의 빈 코드 한 줄이 서비스 전체의 기밀성을 무너뜨리는 구멍이 됩니다. 상용 서버와 통신할 때는 반드시 공인된 CA로부터 발급받은 인증서를 사용하고, 코드 수준에서는 시스템의 검증 로직을 믿고 맡기십시오. logger.debug()를 통해 연결 실패 사유를 기록하되, 공격자가 제시한 가짜 인증서를 "괜찮다"며 수용하는 실수를 범하지 않는 것이 네트워크 보안의 핵심입니다.

 
    • 글자 크기
브라우저가 종료되어도 남는 흔적: CWE-539 지속성 쿠키 정보 노출 (by suritam9) 믿음의 근거를 확인하라: CWE-347 부적절한 전자서명 확인 (by suritam9)

댓글 달기

첨부 (0)
위로