인디노트

OpenSSL API를 이용한 보안 프로그래밍, Part 2: 안전한 핸드쉐이크(handshake) (한글) 본문

인증기술/PKI 기술

OpenSSL API를 이용한 보안 프로그래밍, Part 2: 안전한 핸드쉐이크(handshake) (한글)

인디개발자 2018. 10. 8. 08:30

OpenSSL API를 이용한 보안 프로그래밍, Part 2: 안전한 핸드쉐이크(handshake) (한글)

man in the middle (MITM) 공격 피하기

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

Kenneth Ballard, Software Engineer, MediNotes Corp.

2007 년 9 월 18 일

Secure Sockets Layer (SSL) 세션 중에 핸드쉐이크(handshake)를 보안화 하는 것은 중요합니다. 이 연결에 개입된 모든 보안들이 핸드쉐이크 내부에서 설정되기 때문입니다. 믿을 수 있는 소스인 것처럼 가장하여 침입하는 man in the middle (MITM) 공격에서 SSL 핸드쉐이크를 보안화 하는 방법을 배워봅시다. 디지털 인증서 개념과 OpenSSL API가 이들을 다루는 방법도 설명합니다.
소셜 북마크

mar.gar.inmar.gar.in
diggDigg
del.icio.usdel.icio.us
SlashdotSlashdot

얼마 전까지만 해도, 안전한 핸드쉐이크는 두 당사자들간 비즈니스가 건전한 토대에서 진행되고 있다는 표시었다. 결국, 핸드쉐이크는 잠재적인 파트너를 평가하는 기회였다. 안전하고 믿을 수 있는 핸드쉐이크는 두 트랜잭션 당사자들이 서로에게 유익이 되는 무엇인가를 수행하고 있다는 것을 의미했다. 안전하지 않은 핸드쉐이크는 한 쪽만 이 트랜잭션을 이해하고 있다는 것을 의미했다.

핸드쉐이크는 온라인 트랜잭션에서도 똑같이 적용된다.

developerWorks의 이전 기술자료인, "OpenSSL API를 이용한 보안 프로그래밍, Part 1: API의 개요에서는 OpenSSL을 사용한 기본적이고 단순한 보안 연결을 생성하는 방법을 설명했다. 하지만, 기본 설정에 포함된 기초적인 부분만 설명했다. 커스터마이징 방법에 대해서는 설명하지 못했다. 이전 글에서는 디지털 인증서의 개념을 설명했고, OpenSSL의 인증서 확인이 성공 또는 실패했는지를 확인하는 방법을 설명했다.

이 글에서는 OpenSSL에 대해 좀더 자세히 살펴볼 예정이다. man in the middle (MITM) 공격에 대비하도록 핸드쉐이크를 보안화 하는 방법을 설명할 것이다.

디지털 인증서

이 글 후반에, 디지털 인증서를 검색하고 확인하는 방법을 설명할 것이므로, 여기에서는 디지털 인증서가 무엇이고 어떻게 작동하는지에 대해서 간략히 설명하겠다. 여러분이 데이터 암호화와 SSL에 대해 익숙하다면, 이 섹션을 읽지 않아도 좋다. 암호와 SSL 실행에 대해 배우고 싶다면 참고자료 섹션에 소개된 기술자료와 튜토리얼을 참조하라.

간단히 말해서, 디지털 인증서는 비대칭적으로 암호화 된 키(asymmetric cryptography key)이다. 디지털 인증서와 관련한 현재의 표준은 이 키와 함께 정보를 포함시켰다. 전형적인 디지털 인증서에는 소유자의 이름(인증서가 웹 서버에서 사용될 경우 전체 도메인 이름)과 연락처 정보가 포함된다. 유효 데이터 범위와 보안 서명이 있어서 그 인증서가 오염되었는지 여부를 확인할 수 있다.

디지털 인증서는 명령어 OpenSSL 툴 또는 이와 같은 목적에 맞게 만들어진 기타 툴을 사용하여 쉽게 생성될 수 있다. 누군가에 의해 생성된 디지털 인증서는 신뢰도에 문제가 생길 것이다. 디지털 인증서는 단순한 암호 키 이상이다. 이것은 온라인 증명서이다. 인증서는 여러분과 통신을 시도하는 누구든 확인한다. 신뢰를 보여주려면 디지털 인증서는 Certificate Authority (CA)에 의해 서명이 되어야 한다.

인증서 기구는 디지털 보안 세계에서 신임을 받는 서드 파티로서 작동한다. 온라인에서 정체를 확인하는 것은 어렵기 때문에, 인증서 기구가 그 일을 떠맡는다. 인증서를 구매하거나 인증서 서명에 지불을 하는 사람의 신원을 확인한다. 인증서를 신임하려면, 사용자는 인증서 기구를 신임해야 한다. 사용자는 CA의 트러스트 인증서를 처리 및 사용함으로써 인증서 기구의 신뢰성에 서명한다. Verisign과 Thawte가 잘 알려진 인증서 기구이다.

인증서의 보안이 노출되면, 취소된다. 다시 말해서 무효한 것으로 선언된다. 인증서가 무효한 것으로 선언되면, CA는 그 인증서 카피를 소유하고 있는 모든 사람들에게 공지를 보내야 한다. 대신, CA는 Certificate Revocation List (CRL)를 발행한다. 디지털 인증서를 사용하는 브라우저와 기타 보안 애플리케이션들은 그 인증서가 소유자 또는 CA에 의해 취소되었는지를 확인할 수 있다.

인증서 철회는 OCSP 프로토콜을 사용하여 검사될 수 있다. OCSP는 Online Certificate Status Protocol을 뜻하는 말로서, RFC 2560에서 정의된다. OpenSSL은 OCSP와 CRL 기능 모두를 갖고 있지만, 그 기능까지는 이 글에서는 설명하지 않겠다. 디지털 인증서에 대한 현재 표준은 X.509이다. (RFC 3280에서 정의됨)




위로


비즈니스를 시작하는 핸드쉐이크

이 글은 핸드쉐이크 하는 사이에 서버의 디지털 인증서를 처리하는 것에 초점을 맞추므로 핸드쉐이크가 어떻게 작동하는지를 상세히 살펴보고자 한다. 여러분이 SSL 프로시저에 익숙하다면, 이 섹션은 무시해도 좋다.

연결에서 개방(opening) 핸드쉐이크는 서버에 "Hello"라고 말하는 클라이언트로 시작된다. Hello 메시지에는 클라이언트의 보안 매개변수들이 포함되어 있다.

  • SSL 버전 넘버
  • 무작위로 생성된 데이터
  • 암호 설정
  • 통신에 필요한 기타 사항

서버는 클라이언트가 제공한 것과 같은 유형의 정보인 서버의 보안 매개변수들을 포함하고 있는 고유의 Hello 메시지에 응답한다. 바로 이때 서버도 디지털 인증서를 보낸다. 클라이언트 권한이 연결에 사용되면 서버는 클라이언트의 인증서에 대한 요청을 보낸다.

서버의 Hello 메시지를 받으면, 디지털 인증서가 확인된다. 인증서의 다양한 매개변수들을 확인하여 인증서가 원래 그대로 보존되었는지를 확인하고, 유효 기간 내에 인증서가 사용되고 있는지를 확인한다.

여기에서 수행되어야 하는 한 가지 단계는 연결에 사용되는 호스트 네임과 비교하여 인증서 상의 이름을 검사하는 것이다. 이는 SSL 표준의 일부는 아니지만, man in the middle attack (MITM)을 방지하기 위해서 권장된다. 이 단계는 인증서가 여러분이 생각하고 있는 엔터티에서 온 것임을 확인한다. 이 두 개가 이 지점에서 매치되지 않으면, 인증서는 무효한 것이 아닌, 의심의 대상이 된다.

클라이언트와 서버 간 공유되었던 랜덤 데이터는 서버와 클라이언트에게만 알려진 공유 비밀 값으로서 이 세션에만 사용되는 premaster secret을 생성하는데 사용된다. 이 비밀 값은 서버의 디지털 인증서로 암호화 되고 확인을 위해 서버로 보내진다.

서버가 클라이언트 인증을 요청하면, 클라이언트는 핸드쉐이크 동안 무작위로 생성되고 서버와 클라이언트에게만 알려진 데이터의 단방향 해시(hash)를 생성한다. 클라이언트는 클라이언트의 개인 키(private key)를 사용하여 해시에 서명을 하고 서명된 데이터와 디지털 인증서를 서버로 보낸다. 서버는 그 정보를 사용하여 클라이언트를 인증한다.

인증이 성공하면, 서버와 클라이언트 모두 공유된 랜덤 데이터를 알고리즘을 통해 실행하여 master secret을 생성한다. master secret에서, 클라이언트와 서버는 세션 키(session keys)를 생성한다. 이것은 선택된 시메트릭 암호 안에 있는 대칭 키로서 세션 데이터를 암호화 하는데 사용된다.

클라이언트는 종료되었다는 메시지를 서버로 보냄으로써 핸드쉐이크를 종료한다. 이것은 서버에 의해 확인되어야 하는 암호화 된 단방향 해시 값 세트이다. 서버는 비슷한 메시지를 클라이언트로 보낸다. 클라이언트와 서버는 핸드쉐이크를 종료하고 통신을 시작하기 전에 데이터가 정확한지를 확인한다.




위로


Man in the middle

이것은 아이들이 하는 게임이지만, public key infrastructures (PKI)에서도 발생할 수 있는 심각한 공격이다. 디지털 인증서에 대해 이야기 할 때, SSL 연결 뒤의 보안 매개변수와 관계 없이 man in the middle 공격은 이러한 사전 주의 사항들을 실행하기 때문에 반드시 고려해야 한다.

Casey와 Samantha가 SSL을 사용하여 통신한다고 생각해 보자. 삼자인 Isabel은 연결 시도를 가로채서 이들 간 프록시로서 역할을 한다. Isabel이 SSL 연결이 설정되었다는 것을 알면, Samantha에게는 Casey인 것처럼, Casey에게는 Samantha인 것처럼 가장한다. Isabel은 중간에서 양 측의 대화를 가로챈다. 대화에 계좌 번호와 개인 정보가 포함되어 있다면 Isabel은 아이디 도용도 할 수 있다.

이 부분에서 인증서에 대한 트러스트 체인과 공통 이름들이 이를 방지할 수 있다. 핸드쉐이크 동안, 인증서가 교환된다. 인증서의 유효성이 분석될 때, 트러스트 서명도 검사된다. 서버 인증서 상의 공통 이름이 나머지 인증서와 함께 검사되면 공격은 실패하지 않겠는가? 전적으로 그런 것은 아니다.

Isabel이 Samantha의 이름을 가진 인증서를 갖고 있다고 가정해 보자. 이것은 또한 Casey의 트러스트 모델 내에서 CA에 의해 서명도 되었다. 여기에서 MITM 공격은 공통 이름을 검사한다고 해서 실패되지 않는다. 이 인증서와 트러스트는 유효하고, 이름이 체크된다. 이제 큰 문제가 생긴 것이다.

하지만, 인증서 기구를 보면 이것은 별개의 문제이다. 대부분의 인증서 기구는 개인의 이름을 가진 디지털 인증서를 발행하기 전에 개인의 신분을 확인한다. 이것 때문에, Isabel이 잘 알려진 매우 명성이 높은 인증서 기구에서 Samantha의 이름을 가진 디지털 인증서를 얻을 가능성은 희박하다.

CA를 사용하면 이러한 보안 문제들을 보다 쉽게 해결할 수 있다. 예를 들어, CA와 Isabel이 같은 컴퍼니에 적용한다면 (다시 말해서, "inside job"). 이렇게 되면, 거의 모든 사람들의 이름을 가진 임의의 인증서를 생성할 수 있는 CA의 작업 공간 내에서 서명 키가 누군가에 의해서 도난 당할 수 있다. 인증서의 개인적인 부분은 서명을 생성하는데 사용되는 반면, 패스워드는 소셜 엔지니어링 또는 기타 비슷한 기술을 사용하여 도난 당할 수 있다.

MITM 공격은 프록시 서버(proxy servers)를 논할 때 특히 중요하다. 보안 연결은 프록시 서버를 통해 의도한 목적지로 "터널링" 되어야 하므로, 악의적인 프록시 서버들은 어떤 대화라도 쉽게 엿들을 수 있다. 악의적인 프록시는 연결이 실제로 이루어 지지 않을 때도 연결이 된 것처럼 보이게 할 수 있다. 인터넷을 통해 "익명 프록시(anonymous proxy)" 서비스를 사용할 때 특히 주의를 기울여야 한다. 그들의 시스템을 통해 사용자 이름과 패스워드를 보낸다고 해서 충분히 신뢰할 수 있겠는가?

이러한 공격은 컴퓨터 및 디지털 보안의 세계에서만 발생하는 것이 아니다. 한 여성이 MITM 공격과 비슷한 기술을 사용하여 가족들에게서 많은 돈을 훔쳤다. (참고자료).




위로


OpenSSL과 디지털 인증서

OpenSSL은 디지털 인증서 전용의 내부 라이브러리를 갖고 있다. 여러분이 OpenSSL의 소스 코드를 사용하고 있다고 가정하고, 이 소스 코드는 crypto/x509와 crypto/x509v3 디렉토리 밑에 있다. 이 소스 코드는 디지털 인증서를 다루는 여러 구조들을 정의한다. 표 1을 보자.

표 1. X.509 인증서와 관련한 OpenSSL 구조

구조기능
X509디지털 인증서와 관련한 모든 데이터를 포함하고 있다.
X509_ALGOR인증서가 디자인 될 수 있는 알고리즘을 제공한다.
X509_VAL인증서의 유효 기간.
X509_PUBKEY인증서(특히 RSA 또는 DSA)의 공개 키 알고리즘.
X509_SIG인증서의 해시 서명.
X509_NAME_ENTRY인증서가 포함하고 있는 다양한 데이터 필드용 엔트리들.
X509_NAME네임 엔트리 스택 포함.

이는 구조의 일부일 뿐이다. OpenSSL 내에서 사용되는 대부분의 X.509 구조들은 애플리케이션에서는 사용되지 않는다. 위에 열거된 구조들 중에서, 이 글에 사용되는 두 가지는 X509와 X509_NAME이다.

디지털 인증서를 다루고 처리하는데 사용되는 다양한 함수들이 구조에 존재한다. 이 함수들은 이 함수들이 적용되는 구조의 이름을 따른다. 예를 들어, X509_NAME으로 시작되는 이름을 가진 함수는 X509_NAME 구조에 적용된다. 이러한 함수들은 필요한 만큼 사용될 것이다.




위로


고유의 트러스트 인증서 제공하기

인증서의 신뢰성을 검사하기 전에 기본적인 신용 인증서 세트가 보안 연결을 위해 OpenSSL을 설정하는 동안 생성된 SSL_CTX 객체에 제공되어야 한다. 이것은 여러 가지 방식으로 제공될 수 있지만, 가장 쉬운 방법은 인증서를 PEM 파일 인증서를 사용하고 SSL_CTX_load_verify_locations(ctx, file, path);를 사용하여 OpenSSL에 로딩한다. file은 PEM 포맷으로 한 개 이상의 인증서를 포함하고 있는 파일에 대한 경로이다. path는 PEM 포맷으로 된 한 개 이상의 파일들에 대한 디렉토리 경로이지만, 파일 이름은 특정 포맷이어야 한다. 하나의 PEM 파일로 트러스트 인증서를 갖는 것이 더 쉬우며, 경로 인자를 NULL로서 유지한다: SSL_CTX_load_verify_locations(ctx, "/path/to/trusted.pem", NULL);.

하나의 폴더에 개별 파일일 때 트러스트 인증서를 추가 및 업데이트 하는 것이 더 쉽지만, 트러스트 인증서를 그렇게 자주 업데이트 하지는 않는다.




위로


인증서 확인하기

통신이 지속되거나 인증서가 검색되기 전에, SSL_get_verify_result()를 사용하여 OpenSSL의 내부 인증서 확인의 결과를 확인한다. SSL_get_verify_result()가 X509_V_OK 이외의 코드를 리턴한다면, 인증서가 무효하다는 것을 의미하는 것일까? 이는 리턴 코드에 따라 다르다.

일반적으로 리턴 코드가 X509_V_OK가 아니라면, 이 인증서와 관련하여 인증서 또는 보안에 문제가 있는 것이다. 한 가지만 명심하라. 인증서를 확인할 때 OpenSSL이 수행하지 않는 몇 가지 보안 체크가 있다. 인증서의 공용 이름을 체크 및 확인하는 취소도 여기에 포함된다.

SSL_get_verify_result()에 대한 리턴 코드는 apps 밑에 있는 verify에 문서화 되어 있다. 일부 코드들은 사용되지 않는 것으로 리스팅 되는데, 이들은 절대로 리턴되지 않음을 의미한다. 일부 코드들은 매우 중요하고, 일부는 그렇지 않다. 예를 들어, 트러스트 인증서 스토어가 로딩되지 않았기 때문에 트러스트가 확인될 수 없다면, 통신의 지속 여부는 개발자에 달려있다.

확인의 결과가 어떻든지 간에, 잠재적으로 불안한 보안 매개변수들로 지속하는 것은 개발자의 몫이다. 인증서가 매우 불안하면 에러 리턴 코드들이 주어진다.




위로


피어(peer) 인증서 검색하기

인증서를 사용자에게 디스플레이 하거나 호스트 네임 또는 인증서 기구에 비교하여 이를 확인해야 한다면 피어(peer) 인증서를 가져와야 한다. 테스트 결과를 확인한 후에 인증서를 가져오려면, SSL_get_peer_certificate()를 호출한다. 이것은 X509 포인터를 인증서에 리턴하고, 어떤 인증서도 제공되지 않을 경우 NULL을 리턴한다. (Listing 1).


Listing 1. 피어 인증서 가져오기 
                
X509 * peerCertificate;


if(SSL_get_verify_result(ssl) == X509_V_OK)
    peerCertificate = SSL_get_peer_certificate(ssl);
else
{
    /* Handle verification error here */
}




위로


피어 인증서 확인하기

핸드쉐이크에서 제공된 서버의 인증서는 서버의 호스트 네임과 매치하는 것에 대한 이름을 가져야 한다. 그렇지 않다면, 인증서는 의심스러운 것으로 표시되어야 한다. 내부 확인 절차는 이미 트러스트와 종료에 대해 인증서를 검사한다. 인증서가 종료되었거나 믿을 수 없는 서명을 포함하고 있다면 무효로 표시된다. 이것은 SSL 표준의 일부가 아니므로 OpenSSL은 호스트 네임에 대해 인증서의 이름을 검사하지 않는다.

인증서의 "이름"은 실제로 인증서 상의 Common Name 필드이다. 이 필드는 인증서에서 가져온 것이어야 하며 호스트 네임에 비교하여 확인되어야 한다. 이 두 가지가 매치되지 않으면, 인증서는 무효가 아닌 의심 상태가 된다. Yahoo! 같은 일부 기업들은 다양한 호스트에 같은 인증서를 사용한다. 그 인증서에 대한 Common Name이 단 한 개의 호스트를 위한 것인데도 말이다. 인증서가 같은 회사에서 온 것인지를 확인하기 위해 보다 심도 깊은 체크가 수행되지만, 이는 프로젝트의 보안 필요에 따라 수행한다.

인증서에서 공통 이름을 가져오는 단계는 다음과 같다.

  • 인증서 구조에서 X509_NAME 객체를 가져온다.
  • X509_NAME 객체에서 이름을 가져온다.

X509_get_subject_name()을 사용하여 인증서에서 X509_NAME 구조를 가져온다. 이것은 X509_NAME 객체에 대한 포인터를 리턴한다. 여기서부터 X509_NAME_get_text_by_NID()를 사용하여 공통 이름을 스트링으로 가져온다. (Listing 2).


Listing 2. Common Name 검색 및 확인 
                
char commonName [512];
X509_NAME * name = X509_get_subject_name(peerCertificate);
X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512);

/* More in-depth checks of the common name can be used if necessary */

if(stricmp(commonName, hostname) != 0)
{
    /* Handle a suspect certificate here */
}

표준 C 스트링 함수 또는 각자 개인이 선호하는 스트링 라이브러리를 사용하여, 공통 이름과 호스트 이름을 비교한다. 미스매치(mismatch)를 다루는 방법은 프로젝트의 요구 사항이나 사용자의 결정에 달려있다. 더 심도 깊은 체크가 사용되어야 한다면, 개별 스트링 라이브러리를 사용하여 복잡함을 없애라고 권고하고 싶다.




위로


신뢰 얻기

이 글을 통해 신뢰를 받은 소스인 것처럼 가장하여 침입하는 man in the middle 공격에 대하여 SSL 핸드쉐이크를 보안화 하는 방법을 설명했다. 또한 디지털 인증서의 개념과 OpenSSL API가 이들을 다루는 방법도 설명했다.

SSL 세션 동안 핸드쉐이크를 보안화 하는 것은 매우 중요하다. 연결에 포함된 거의 모든 보안들이 핸드쉐이크 내에서 설정되기 때문이다. 이 글에 제시된 각 단계를 따라가는 것은 프로젝트의 보안 요구 사항 및 개발자의 결정에 달려있다.



참고자료



필자소개

Kenneth는 MediNotes Corp.(West Des Moines, Iowa)의 소프트웨어 엔지니어이다. 페루, 나브라스카에 있는 Peru State College에서 경영학 학사 학위를 받았다. Southwestern Community College에서 컴퓨터 프로그래밍의 Associate of Science도 보유하고 있다. 여러 애플리케이션 및 프로그래밍 라이브러리를 작성했다.



반응형
Comments