OWASP Night 8thに行ってきて、iOSやAndroidのいろんなアプリのHTTPS接続の検証がいい加減という話を聞いてきたんですが、それよりもAndroidのデフォルトブラウザChromeが失効検証をしない(=証明書の取り消し確認をしない)という事だって十分問題なんじゃないかなぁとか思ったりしてました。

そういえば以前、秋山さんにAndroidだとHTTPS通信がRC4-MD5になっちゃうという話を聞いて、実際にAndroid上のChromeやOperaやFireFoxを見て全く問題ないことを確認し「よしよし安心だ」などと思っていました。

あれれ?待てよ、秋山さんが言っていたのはブラウザではなく、APIで呼び出したときのCipherSuiteの順序関係の可能性もあるなと思ってグーグル先生に聞いて調べてみました。

(文献1)Why Android SSL was downgraded from AES256-SHA to RC4-MD5 in late 2010 (2013.10.13)
http://op-co.de/blog/posts/android_ssl_downgrade/
(文献2)The Register: Android security relies on ZOMBIE CRYPTO, argues infosec pundit (2013.10.16) http://www.theregister.co.uk/2013/10/16/zombie_cipher_list_gives_android_weak_encryption/

あらら、こりゃやべーよ。なんか検索してみても日本では誰も騒いでないみたいだよ。一ヶ月も放っぽりぱなしだったんだなぁ。自分が第一発見者なら公開せずJPCERTとかに報告するんだけどグローバルには公表から1ヶ月も経っているので、国内で騒がれてないようだから書いとくかなと。

結論から言うと、Java 1.4から1.6ぐらい、Android 2.3以降の全てのバージョンで、ウェブブラウザではなくHttpURLConnectionやApacheのHttpClientなどのクラスのAPIを使ったクライアント実装でHTTPS接続する場合には、デフォルトではRC4-MD5といったとても弱い暗号が最優先で使われるために、「必ず」明示的に暗号設定を指定する必要がある。

という事です。JavaやAndroidのHTTPSクライアントを使用する開発者の方は是非対応をお願いしたいです。

AndrodのHttpURLConnectionクラスの利用

早速、簡単なAndroidのHTTPSクライアントをHttpURLConnectionクラスを使って書いて接続し、CipherSuitesを確認してみました。Android 4.0.3で確認しましたが、やはりRC4-MD5が優先になっていました。文献1によればAndroid 2.3.4以降、Android 4.3までダメなようです。

Client Hello Version: TLS 1.0
TLS Version: TLS 1.0
Num Cipher Suites: 35
Cipher Suite: TLS_RSA_WITH_RC4_128_MD5 (0x0004)
Cipher Suite: TLS_RSA_WITH_RC4_128_SHA (0x0005)
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
Cipher Suite: TLS_ECDH_ECDSA_WITH_RC4_128_SHA (0xc002)
Cipher Suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA (0xc004)
Cipher Suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA (0xc005)
Cipher Suite: TLS_ECDH_RSA_WITH_RC4_128_SHA (0xc00c)
Cipher Suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA (0xc00e)
Cipher Suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA (0xc00f)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_RC4_128_SHA (0xc007)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
Cipher Suite: TLS_ECDHE_RSA_WITH_RC4_128_SHA (0xc011)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033)
Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039)
Cipher Suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA (0x0032)
Cipher Suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA (0x0038)
Cipher Suite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x000a)
Cipher Suite: TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA (0xc003)
Cipher Suite: TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA (0xc00d)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA (0xc008)
Cipher Suite: TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (0xc012)
Cipher Suite: TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA (0x0016)
Cipher Suite: TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA (0x0013)
Cipher Suite: TLS_RSA_WITH_DES_CBC_SHA (0x0009)
Cipher Suite: TLS_DHE_RSA_WITH_DES_CBC_SHA (0x0015)
Cipher Suite: TLS_DHE_DSS_WITH_DES_CBC_SHA (0x0012)
Cipher Suite: TLS_RSA_EXPORT_WITH_RC4_40_MD5 (0x0003)
Cipher Suite: TLS_RSA_EXPORT_WITH_DES40_CBC_SHA (0x0008)
Cipher Suite: TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA (0x0014)
Cipher Suite: TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA (0x0011)
Cipher Suite: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff)

例えばAndroid 4.1のNativeCrypto.javaのソースコードを見ると確かにRC4-MD5が優先になっているように見えます。

Oracle JavaのHttpURLConnectionクラスの利用

文献1によれば、そのようなCipherSuiteのデフォルト設定はOracle(Sun) Java JRE/JDKの 設定から来ているもので、Java 1.4.2_19, 1.5.0 (2004)から Java 1.6 (2006)まで 弱いRC4-MD5が優先されていると書いています。

Windows版Sun JDK 1.6.0_21 b07では以下のようになっていました。確かにRC4-MD5優先です。

Client Hello Version: SSL 2.0
Client Hello Version: TLS 1.0
Num Cipher Suites: 19
Cipher Suite: TLS_RSA_WITH_RC4_128_MD5 (0x000004)
Cipher Suite: SSL2_RC4_128_WITH_MD5 (0x010080)
Cipher Suite: TLS_RSA_WITH_RC4_128_SHA (0x000005)
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x00002f)
Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x000033)
Cipher Suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA (0x000032)
Cipher Suite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x00000a)
Cipher Suite: SSL2_DES_192_EDE3_CBC_WITH_MD5 (0x0700c0)
Cipher Suite: TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA (0x000016)
Cipher Suite: TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA (0x000013)
Cipher Suite: TLS_RSA_WITH_DES_CBC_SHA (0x000009)
Cipher Suite: SSL2_DES_64_CBC_WITH_MD5 (0x060040)
Cipher Suite: TLS_DHE_RSA_WITH_DES_CBC_SHA (0x000015)
Cipher Suite: TLS_DHE_DSS_WITH_DES_CBC_SHA (0x000012)
Cipher Suite: TLS_RSA_EXPORT_WITH_RC4_40_MD5 (0x000003)
Cipher Suite: SSL2_RC4_128_EXPORT40_WITH_MD5 (0x020080)
Cipher Suite: TLS_RSA_EXPORT_WITH_DES40_CBC_SHA (0x000008)
Cipher Suite: TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA (0x000014)
Cipher Suite: TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA (0x000011)

これが、Oracle(Sun) Java JRE/JDK 1.7.0 b147だと以下のようになり、 DHE-RSA-AES128-SHAが優先されており、1.7のとあるバージョンからは問題が直っているのだと思います。

Client Hello Version: TLS 1.0
TLS Version: TLS 1.0
Num Cipher Suites: 21
Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033)
Cipher Suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA (0xc004)
Cipher Suite: TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA (0x0016)
Cipher Suite: TLS_RSA_WITH_RC4_128_SHA (0x0005)
Cipher Suite: TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA (0xc003)
Cipher Suite: TLS_ECDHE_RSA_WITH_RC4_128_SHA (0xc011)
Cipher Suite: TLS_ECDH_ECDSA_WITH_RC4_128_SHA (0xc002)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_RC4_128_SHA (0xc007)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA (0xc008)
Cipher Suite: TLS_ECDH_RSA_WITH_RC4_128_SHA (0xc00c)
Cipher Suite: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff)
Cipher Suite: TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA (0xc00d)
Cipher Suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA (0xc00e)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
Cipher Suite: TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (0xc012)
Cipher Suite: TLS_RSA_WITH_RC4_128_MD5 (0x0004)
Cipher Suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA (0x0032)
Cipher Suite: TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA (0x0013)
Cipher Suite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x000a)

この問題を解決するには開発側でなんとかするしかない

この問題を解決するにはHttpURLConnectionで明示的にCipherSuiteやSSL/TLSのバージョンを 指定してやる必要があります。SSLSocketクラスでsetEnablesCipherSuites()メソッドを使う プリミティブな方法もありますが、プロパティで設定する方が簡単かもしれません。

System.setProperty("https.cipherSuites", 
                   "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA," +
                   "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
System.setProperty("https.protocols", "TLSv1.2,TLSv1.1");
URL url = new URL("https://www.verisign.com/");
BufferedReader in = 
  new BufferedReader(new InputStreamReader(url.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
  System.out.println(inputLine);
in.close();

国内でもこの問題が広く認知され、対応されるといいですね。

今日はこの辺で