Tag Archives: https

Google Play 안전하지 않은 TrustManager 경고 대응하기

logo-google-play-vetor

구글이 기존의 수많은 앱들의 X509TrustManager를 무력화 시켜 구현한 것에 대한 대응을 나선 것 같습니다. 현재 HTTPS 통신을 위해 SSL 관련된 코드를 구현하였지만 그 방법이 잘못된 경우에 대해 구글 플레이에서 다음과 같은 경고가 뜨고 있습니다.

앱이 Apache HTTP 클라이언트가 있는 X509TrustManager 인터페이스의 안전하지 않은 구현을 사용하고 있어 보안 취약성에 노출되었습니다. 취약성 수정 마감일 등 자세한 내용은 Google 도움말 센터의 이 도움말을 참조하세요

영문판의 경우의 경고 메시지는 다음과 같습니다.

Your app is using an unsafe implementation of the X509TrustManager interface with an Apache HTTP client, resulting in a security vulnerability. Please see this Google Help Center article for details, including the deadline for fixing the vulnerability.

구글플레이에 앱을 출시하고 5시간(저의경우에는 하루정도 뒤에 확인 가능했었습니다)정도 뒤에 구글플레이에 앱 리스트에 경고 표시가 다음과같이 표기됩니다.

google_play_warning_x509trustmanager

이는 다음과 같은 코드가 앱내에 존재할 경우 발생하는 경고이며 2016년 5월 17일부터 해당 문제가 해결되지 않은 앱들을 차단할 예정이라고 합니다.

문제가 되는 부분은 checkServerTrusted 메소드의 내부 구현이 아무것도 없는것에서 기인합니다; 정확하게는 위의 코드는 SSL의 동작을 완벽하게 무력화 시켜 인증 여부를 보장할 수 없는 누구와도 통신할 수 있게 된 상태를 야기합니다. 만약 TrustManager를 커스터마이징해서 사용해야 하는 경우  내부에 원치 상황에 대해 CertificateException 또는 IllegalArgumentException 예외를 발생시키는 코드를 구현해야 합니다.

우선 문제가 되는 Apache의 HttpClient를 사용하는 올드한 형태의 예제를 확인해 보겠습니다.

그리고 다음의 코드는 개발하신 스타일에 따라 다른 모습이겠지만 SSLSocketFactory와 X509TrustManager를 구현한 코드입니다. 위의 코드에서 MySSLSocketFactory라는 이름으로 가져다 사용하고 있습니다.

SSLSocketFactory를 상속하여 내부적으로 커스터마이징 된 X509TrustManager를 사용한 SSLContext를 통하여 소켓을 생성하도록 구현되어있습니다. 이렇게 생성한 SSL Socket은 보안에 취약한 상태입니다.

사실 정상적인 CA가 발급한 SSL 인증서로 구축된 서버라면 다음과 같은 코드만으로 통신에 문제가 없습니다.

하지만 TrustManager를 무력화 시키면서까지 HTTPS 통신을 하려 했다는건 인증서 비용등의 문제로 자체 발급한 Self-Signed 인증서를 사용중이기 때문이 아닐까 생각됩니다. 확인 할 수 없는 인증서로 인한 통신 불가 상황에서는 다음과 같은 예외가 발생합니다.

이 문제를 해결하는 가장 간단한 방법은 정상적인 CA가 발급한 인증서를 구매하여 서버에 채택하고 위쪽의 예제 코드처럼 SSLSocketFactory 를 기본 형태로 사용하는 방법입니다.

하지만 여전히 자체 발급한 인증서로도 구글의 권고안을 따라 문제를 해결할 수 있습니다. TrustManager를 재구현하면서 무력화 시킨 코드가 문제가 되기 때문에 TrustManager를 무력화 시키지 않는 방법으로도 해결을 해보도록 하겠습니다.

우선 자체 발급한 인증서는 일반적인 공인된 CA들이 인증해 주지 않는다는것이 중요하므로 자체적인 CA의 인증서를 앱내에 탑재하여 인증을 가능케 하는 방법이 있습니다. 인증서 자체 발급에 대해서는 [Self-Signed SSL로 Apache HTTPS 구현하기]를 참고해주시기 바랍니다.

앱내에 탑재될 인증서는 CA의 PEM 포맷의 인증서이며 위의 참고글에서 언급되는 rootCA.crt 파일이 여기에 해당됩니다. 이 인증서의 내용은 BASE64 인코딩이 되어있는 형태입니다. 이것을 이용하여 코드를 수정해 보겠습니다.

다른 많은 예제들은 인증서를 앱내에 파일로 포함하여 FileInputStream등으로 읽어서 쓰는 예시가 많았습니다만 파일을 앱에 포함할 정도면 텍스트를 그대로 하드코딩 해도 문제가 되지 않겠다고 생각되어 위와 같이 작성 해 보았습니다. 위에서 언급되었던 잘못된 MySSLSocketFactory의 구현은 다음과 같이 변경되었습니다.

위와 같이 수정 후 통신 테스트를 해보면 잘 되는 것을 확인할 수 있습니다. 해당 CA로 부터 발급된 모든 인증서가 적용된 서버들과 통신이 문제없이 가능합니다. 만약에 지정한 CA 인증서와 연관이 없는 인증서가 적용된 서버일 경우 다음과 같은 오류가 발생합니다.

만약에 서버의 CA인증서 존재 여부가 불분명하고 자체 발급하여 사용중인 인증서 역시 확인하기가 어렵다면 아쉬운대로 다음과 같은 명령을 사용하여 자체 발급된 인증서를 확인 하실 수 있습니다.

s_client 명령을 사용하여 통신하고자 하는 서버의 인증서를 확인 할 수 있습니다. 이는 CA 인증서는 아니지만 이렇게 조회된 인증서를 이용해도 1:1간의 통신에는 문제가 없습니다.

문제를 해결하고 Version Code를 올린 APK를 재등록하고 하루정도가 지나면 해당 경고메시지가 사리지는것을 확인하실 수 있습니다.

스크린샷 2016-02-22 오후 3.26.24

참고 :

  • https://support.google.com/faqs/answer/6346016
  • https://developer.android.com/intl/ko/training/articles/security-ssl.html#UnknownCa

Self-Signed SSL로 Apache HTTPS 구현하기

apache-logo

기존의 HTTP(HyperText Transfer Protocol)과 다르게 HTTPS(Hypertext Transfer Protocol over Secure Socket Layer)는 SSL위에서 돌아가는 HTTP의 평문 전송 대신에 암호화된 통신을 하는 프로토콜입니다.

이런 HTTPS를 통신을 서버에서 구현하기 위해서는 신뢰할 수 있는 상위 기업이 발급한 인증서가 필요로 한데 이런 발급 기관을 CA(Certificate authority)라고 합니다. 하지만 이러한 기업의 인증서를 발급받는것은 무료가 아니며 단순히 모바일앱과의 통신이라던가 테스트 목적에서 발급을 받기에는 부담스러운 부분이 있을 수 있습니다.

이런 경우 자체적으로 인증서를 발급하여 사용하는 방법을 고려해 볼 수 있습니다. 이 경우 브라우저 접속시에 보안 경고가 발생하므로 주의하시기 바랍니다. 이 글에서는 CentOS와 Apache 웹서버 구동 환경에서의 HTTPS 구축하는 방법을 정리해 보겠습니다.

우선 위와 같이 openssl과 mod_ssl을 설치해줍니다. 만약 설치가 되어있는 상태라면 무시해도 됩니다. 이번엔 다음과 같은 명령을 사용하여 인증서를 생성해 줍니다.

(선택 1) 한번에 원하는 인증서를 발급하기

따로 원하는 CA를 둘것없이 간편하게 서버에서 사용할 인증서를 발급하는 방법은 다음과 같습니다.

(선택 2) 자체 CA 인증서를 생성하고 이를 통해 인증서를 발급하기

자체적으로 CA를 구축하는 방법도 있습니다. 세상 누구도 알아주지 않겠지만 다양한 서비스를 동시에 운영중이라면 해볼만한 시도라고 생각이 됩니다. 우선 CA 인증서부터 발급합니다.

이번엔 CA에 인증서 발급을 요청하기 위한 CSR(Certificate Signing Request)를 생성합니다.

이번엔 마지막으로 CA 인증서와 CSR을 이용하여 서비스에 사용할 인증서를 발급해 보도록 하겠습니다.

아파치 웹서버 설정에 적용

이제 /etc/ssl/certs 디렉토리에 mycert.key 파일과 mycert.crt 파일이 생성되었습니다. 이제 Apache 설정에 다음과 같은 형태로 이 키를 지정해 줍니다.

이제 Apache 데몬을 재시작 한 뒤 해당 도메인에 https 로 접속하면 다음과 같은 보안 경고가 뜨게 됩니다.

not_secure_on_apache_https

하지만 아래의 안전하지 않음으로 이동을 통해 사이트에 정상적으로 접속하는 것이 가능합니다. 만약 iOS/Android에서 구동되는 어플리케이션의 경우 별도의 처리가 필요할 것입니다.