여러분의 앱은 Android 7.0에서 SSL 통신하고 있습니까?

테스티드는 모바일 앱 테스트 서비스입니다. 모바일 앱 패키지 파일을 다양한 디바이스에 설치하고 소프트웨어가 앱을 자동으로 동작시켜 오류를 찾습니다.
테스티드는 구글과 함께 Android 7.0 디바이스에서 많은 앱을 동작시켜 Android 7.0와 앱에 문제가 있는지 확인하는 캠페인을 하고 있습니다.
Android 7.0 디바이스에서 기존에 출시된 앱을 동작시키면 다양한 문제가 발견됩니다. 그 중 눈에 띄는 것 하나 소개합니다.

몇몇 앱에서 SSL을 이용한 암호화된 통신 부분에 문제가 발견되었습니다.
사용자에게 보여지는 증상으로는, 앱 실행 후 첫 화면에서 프로그레스바가 멈추지 않고 계속 움직이거나 데이터를 저장하는 등의 행위를 했을 때 정상적으로 저장되지 않는 것 등 입니다.
모바일 앱이 시작할 때 서버로부터 데이터를 받아오고자 하는데 데이터를 받아오지 못하니 프로그레스바가 멈추지 않는 것으로 추측됩니다. 데이터를 저장할 때에 디바이스 내부에 저장하는 경우가 아닌 서버에 저장하는 경우에도 동일한 원인일 수 있습니다.

SSL 통신의 문제로 추측되는 사례를 소개합니다. 해당 앱의 불안정함을 들추고자하는 것은 아닙니다. 게다가 테스티드는 개발자 여러분의 노고에 항상 감사하고 있습니다.

1. 알리안츠생명 “모바일센터” v 1.0.9
– https://play.google.com/store/apps/details?id=com.allianz.cssp.app
– 첫 화면에서 “통신상태가 양호하지 않습니다. 네트워크 연결을 확인해주세요.” 메시지 표시되고, “확인” 버튼 누르면 앱 종료됨
– Android 7.0 외에서는 잘 동작함
– 동영상 참고 https://www.youtube.com/watch?v=2c4eO8EfepE

– 관련 로그
12:56:58 ActivityManager: Start proc 22965:com.allianz.cssp.app/u0a2527 for added application com.allianz.cssp.app
<생략>
12:56:59 System.err: javax.net.ssl.SSLHandshakeException: Handshake failed
12:56:59 System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:429)
12:56:59 System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl.java:682)
12:56:59 System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:644)
12:56:59 System.err: at org.apache.http.impl.io.SocketInputBuffer.(SocketInputBuffer.java:75)
12:56:59 System.err: at org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer(SocketHttpClientConnection.java:88)
12:56:59 System.err: at org.apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer(DefaultClientConnection.java:175)
12:56:59 System.err: at org.apache.http.impl.SocketHttpClientConnection.bind(SocketHttpClientConnection.java:112)
12:56:59 System.err: at org.apache.http.impl.conn.DefaultClientConnection.openCompleted(DefaultClientConnection.java:134)
12:56:59 System.err: at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:177)
12:56:59 System.err: at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:169)
12:56:59 System.err: at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:124)
12:56:59 System.err: at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:366)
12:56:59 System.err: at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:560)
12:56:59 System.err: at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:492)
12:56:59 System.err: at com.bd.app.utils.HttpService.doGet(Unknown Source)
12:56:59 System.err: at com.bd.app.utils.HttpSender.httpGet(Unknown Source)
12:56:59 System.err: at com.bd.plugin.app.a.run(Unknown Source)
12:56:59 System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
12:56:59 System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
12:56:59 System.err: at java.lang.Thread.run(Thread.java:761)
12:56:59 System.err: Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0xa89d2c80: Failure in SSL library, usually a protocol error
12:56:59 System.err: error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (external/boringssl/src/ssl/s3_pkt.c:610 0x95ca6480:0x00000001)
12:56:59 System.err: error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO (external/boringssl/src/ssl/s3_clnt.c:764 0xa61fd8de:0x00000000)
12:56:59 System.err: at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
12:56:59 System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357)
12:56:59 System.err: … 19 more

2. BGF리테일 “CU 멤버십” v2.0.20
– https://play.google.com/store/apps/details?id=com.bgfcu.membership
– 첫 화면에서 프로그레스바가 멈추지 않고 계속 돌아감
– 동영상 참조 https://www.youtube.com/watch?v=rwJUYITCQPA

– 관련 로그
10:28:47 ActivityManager: Start proc 5348:com.bgfcu.membership/u0a103 for activity com.bgfcu.membership/.CUIntroActivity
<생략>
10:28:49 Cronos : [F_7156/34069] – ======== mUrl : https://membership.bgfretail.com/appVersion.do========
10:28:49 NetworkSecurityConfig: No Network Security Config specified, using platform default
10:28:49 System.err: javax.net.ssl.SSLHandshakeException: Handshake failed
10:28:49 System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:429)
10:28:49 System.err: at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:406)
10:28:49 System.err: at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:170)
10:28:49 System.err: at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:169)
10:28:49 System.err: at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:124)
10:28:49 System.err: at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:366)
10:28:49 System.err: at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:560)
10:28:49 System.err: at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:492)
10:28:49 System.err: at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:470)
10:28:49 System.err: at com.bgfcu.membership.cronos.http.CNHttpRequest.postClient(CNHttpRequest.java:91)
10:28:49 System.err: at com.bgfcu.membership.cronos.http.CNHttpManager.requestData(CNHttpManager.java:94)
10:28:49 System.err: at com.bgfcu.membership.cronos.asynctask.CNDataAsyncTask.doInBackground(CNDataAsyncTask.java:88)
10:28:49 System.err: at com.bgfcu.membership.cronos.asynctask.CNDataAsyncTask.doInBackground(CNDataAsyncTask.java:1)
10:28:49 System.err: at android.os.AsyncTask$2.call(AsyncTask.java:304)
10:28:49 System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:237)
10:28:49 System.err: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
10:28:49 System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
10:28:49 System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
10:28:49 System.err: at java.lang.Thread.run(Thread.java:761)
10:28:49 System.err: Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0x9bfe9580: Failure in SSL library, usually a protocol error
10:28:49 System.err: error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (external/boringssl/src/ssl/s3_pkt.c:610 0x9bfee2e0:0x00000001)
10:28:49 System.err: error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO (external/boringssl/src/ssl/s3_clnt.c:764 0x9c12b8de:0x00000000)
10:28:49 System.err: at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
10:28:49 System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357)
10:28:49 System.err: … 18 more
10:28:49 Cronos : [F_6883/34069] – httpResponse is null

3. 린나이코리아 “린나이 서비스” v1.2.0
– https://play.google.com/store/apps/details?id=co.kr.rinnai.service
– “출장신청” 버튼 누르면 502 에러코드와 메시지 표시됨
– 동영상 참고 https://www.youtube.com/watch?v=LSFMV9zuihQ

– 관련 로그
4:30:35 ActivityManager: Start proc 30887:co.kr.rinnai.service/u0a1304 for added application co.kr.rinnai.service

4:31:18 System.err: javax.net.ssl.SSLHandshakeException: Connection closed by peer
4:31:18 System.err: at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
4:31:18 System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357)
4:31:18 System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl.java:682)
4:31:18 System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:644)
4:31:18 System.err: at org.apache.http.impl.io.SocketInputBuffer.(SocketInputBuffer.java:75)
4:31:18 System.err: at org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer(SocketHttpClientConnection.java:88)
4:31:18 System.err: at org.apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer(DefaultClientConnection.java:175)
4:31:18 System.err: at org.apache.http.impl.SocketHttpClientConnection.bind(SocketHttpClientConnection.java:112)
4:31:18 System.err: at org.apache.http.impl.conn.DefaultClientConnection.openCompleted(DefaultClientConnection.java:134)
4:31:18 System.err: at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:177)
4:31:18 System.err: at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:169)
4:31:18 System.err: at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:124)
4:31:18 System.err: at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:366)
4:31:18 System.err: at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:560)
4:31:18 System.err: at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:492)
4:31:18 System.err: at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:470)
4:31:18 System.err: at kr.co.iconlab.net.RequestData.requestJson(RequestData.java:195)
4:31:18 System.err: at kr.co.iconlab.net.RequestData.execute(RequestData.java:125)
4:31:18 System.err: at co.kr.rinnai.service.ActivityOutcall$3.doInBackground(ActivityOutcall.java:135)
4:31:18 System.err: at co.kr.rinnai.service.ActivityOutcall$3.doInBackground(ActivityOutcall.java:1)
4:31:18 System.err: at android.os.AsyncTask$2.call(AsyncTask.java:304)
4:31:18 System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:237)
4:31:18 System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
4:31:18 System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
4:31:18 System.err: at java.lang.Thread.run(Thread.java:761)

이 문제의 원인은, SSLv3와 RC4를 Android 7.0가 지원하지 않도록 변경된 것으로 보입니다.
https://security.googleblog.com/2015/09/disabling-sslv3-and-rc4.html
CU 멤버십 앱의 로그에 표시된 URL을 확인해보니, 서버가 지원하는 프로토콜 2개 중에 1개가 SSLv3이고 지원하는 싸이퍼(cipher)도 4개 중에 2개는 RC4입니다.
GeoTrust_BGFRETAIL_20160930

서버가 TLS1.2 등 더 안전한 프로토콜을 이용하도록 변경하는게 좋겠습니다.

위 앱들의 사례와 같이, 기존에 잘 동작하던 앱이Android 7.0에서 변경 사항 때문에 잘 동작하지 않을 수 있습니다. 여러분의 앱도 꼭 확인해보시기 바랍니다.

테스티드는 Android 7.0 디바이스에서 테스트를 제공하고 있습니다. http://testyd.co/ 에 접속하셔서 앱 패키지 파일만 업로드하시면 됩니다.
테스티드를 ucloud biz 웹 사이트에서도 이용할 수 있습니다. ucloud biz에서는 테스티드보다 더 다양한 디바이스를 이용할 수 있습니다. http://ucloudbiz.olleh.com 에 접속하셔서 상품소개 > 모바일 > appster 메뉴로 이동하시면 됩니다.