Database JUNKY

MySQL,MariaDBを中心としたブログです

IPhone アプリ: SSLエラーが発生してから対応したまでの話

f:id:hit10231023:20160526122842j:plain

本日IPhoneアプリケーションエンジニアさんから以下のようなエラーが出て、ファイルがダウンロードできなくなったという連絡を受けました

エラーログ内容

長いので改行しています

2016-05-25 17:56:45.398 TESTAPP[719:391253] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)
2016-05-25 17:56:45.422 TESTAPP[719:391073] [Error] GET '(null)' (0) [nan s]: 
Error Domain=NSURLErrorDomain Code=-1200 "SSLエラーが起きたため、サーバへのセキュリティ保護された接続を確立できません。" 
UserInfo={_kCFStreamErrorCodeKey=-9801, NSLocalizedRecoverySuggestion=それでもサーバに接続しますか?, 
NSUnderlyingError=0x15f0f2930 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" 
UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9801, 
_kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9801}}, 
NSLocalizedDescription=SSLエラーが起きたため、サーバへのセキュリティ保護された接続を確立できません。, 
NSErrorFailingURLKey=https://cdn.sslsitetesttest.com/files/abc.mp4, 
NSErrorFailingURLStringKey=https://cdn.sslsitetesttest.com/files/abc.mp4, 
_kCFStreamErrorDomainKey=3}

特に、こちらのnginxの設定は変更していないし、アプリケーションの問題なんじゃなの?なんて疑いつつ、調査を開始することしました。

nginx実践入門 (WEB+DB PRESS plus)

nginx実践入門 (WEB+DB PRESS plus)

食べる!SSL! ―HTTPS環境構築から始めるSSL入門

食べる!SSL! ―HTTPS環境構築から始めるSSL入門

OpenSSL―暗号・PKI・SSL/TLSライブラリの詳細―

OpenSSL―暗号・PKI・SSL/TLSライブラリの詳細―

エラーの内容をググるとこんな内容が出ました

nlogic.jp

まさしく、この現象だと思うのですが、今まで平気だったのに何で突然?とうところは置いておいて、上記のリンク(すみません、引用させていただきます)

これはApp Transport Security(ATS)という機能らしい。ドキュメントやビデオを見ていると今すぐではないものの今後、HTTPでのリクエストは使えなくしていく気満々な模様。

このATSという機能が発動したのではないかと疑う、、iOSの最近9.3.2にアップデートされたし、それが原因なんじゃないかな?とか勝手に憶測

ATSが発動したら、何すればいい?

これが、全然わからなかった。。リンク先によると無効にする方法を案内してくれているのですが、この変更で、アプリを再度アップするのも難儀なので、なんとか、その先にある WEBサーバ(nginx)側の設定で、アプリを無傷に改修できない調べました。

また、先ほどの引用なのですが。、

ATSについてざっくりまとめるとこんな感じ。

ATSによってHTTPリクエストは自動的にHTTPSリクエストに置き換わる
HTTPSを使用する事を推奨する
TLSv1.2以上に対応している必要がある
上記の例外をInfo.plistに設定することができる

との部分で、「TLSv1.2以上に対応している必要がある」と書いてあったので、nginxのssl.confを確認した。

nginx ssl.conf

ちなみに、ここの設定は環境により違いますので、ssl.confなんてないかもしれないし、あるかもしれない

# pwd                                                
/etc/nginx/conf.d
# 一部抜粋
# vi ssl.conf
----------------------------------------
ssl_protocols  SSLv2 SSLv3 TLSv1;

ふむふむ、、、TLSv1って書いてあるから、これはばっちり、対応しているな。。でわ、ここが原因ではないか。。

運動しながら動画鑑賞!?

ん???

IT・WEB・ソーシャルゲーム業界への転職ならGEEKLY

どうも、この書き方だと、TLSv1.2には対応していないということになるらしい(TLSv1のいう書き方は暗黙的にTLSv1.0とのことっす・・えぇえええええええええ。。。まじで?

結果的に、ssl_protocolsの部分に、 TLSv1.2を追記するとのことなのだが、この TLSv1.2ってopensslのバージョンが1.0.1以上でないと対応していないらしく、おそるおそるバージョンを確認してみた

# openssl version                                    
OpenSSL 1.0.1e-fips 11 Feb 2013

どうやら、そこは対応している模様

おっとnginxのバージョンも関係あるのか。。TLSv1.1とTLSv1.2はnginx 1.1.13以降および1.0.12以降で対応するらしくこちらもnginxのバージョンを確認してみる

# nginx -v
nginx version: nginx/1.8.0

こちらも無事対応している模様・・・よかった・・。

要件は満たしているようなので、nginxのssl.confを以下のように書き直してみました。

# pwd                                                
/etc/nginx/conf.d
# 一部抜粋
# vi ssl.conf
----------------------------------------
ssl_protocols  SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2;
# ssl_protocols  SSLv2 SSLv3 TLSv1;

どうでしょうか?これで、エラーは消えるのでしょうか?ってことで、再度確認してみたところ、エラーがなくなり、動画が無事再生できるようになりました!!!!

ちなみに、SSLが正常かエラーかっていうのは、実機で確認もしたのですが、MACってnscurlという便利ツールがあって、今回、問題のあった、接続先が ATS に対応しているかどうが簡単に調べられます

書式

nscurl --ats-diagnostics https://調べるドメイン名

ssl.conf 修正前(エラー)パターン

TLSv1.0
Result : PASS

以外は、みんなFAIL ・・・

nscurl --ats-diagnostics https://cdn.sslsitetesttest.com
Starting ATS Diagnostics

Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https://cdn.sslsitetesttest.com.
A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error.
Use '--verbose' to view the ATS dictionaries used and to display the error received in URLSession:task:didCompleteWithError:.
================================================================================

Default ATS Secure Connection
---
ATS Default Connection
2016-05-25 21:00:13.736 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:13.737 nscurl[408:4734] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)
Result : FAIL
---

================================================================================

Allowing Arbitrary Loads

---
Allow All Loads
Result : PASS
---

================================================================================

Configuring TLS exceptions for cdn.sslsitetesttest.com

---
TLSv1.2
2016-05-25 21:00:13.847 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:13.848 nscurl[408:4734] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)
Result : FAIL
---

---
TLSv1.1
2016-05-25 21:00:13.862 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:13.863 nscurl[408:4734] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)
Result : FAIL
---

---
TLSv1.0
Result : PASS
---

================================================================================

Configuring PFS exceptions for cdn.sslsitetesttest.com

---
Disabling Perfect Forward Secrecy
2016-05-25 21:00:13.917 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:13.918 nscurl[408:4734] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)
Result : FAIL
---

================================================================================

Configuring PFS exceptions and allowing insecure HTTP for cdn.sslsitetesttest.com

---
Disabling Perfect Forward Secrecy and Allowing Insecure HTTP
2016-05-25 21:00:13.929 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:13.942 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:13.969 nscurl[408:4734] CFNetwork SSLHandshake failed (-9824)
2016-05-25 21:00:13.995 nscurl[408:4734] CFNetwork SSLHandshake failed (-9824)
2016-05-25 21:00:13.996 nscurl[408:4734] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
Result : FAIL
---

================================================================================

Configuring TLS exceptions with PFS disabled for cdn.sslsitetesttest.com

---
TLSv1.2 with PFS disabled
2016-05-25 21:00:14.005 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:14.006 nscurl[408:4734] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)
Result : FAIL
---

---
TLSv1.1 with PFS disabled
2016-05-25 21:00:14.016 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:14.016 nscurl[408:4734] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)
Result : FAIL
---

---
TLSv1.0 with PFS disabled
Result : PASS
---

================================================================================

Configuring TLS exceptions with PFS disabled and insecure HTTP allowed for cdn.sslsitetesttest.com

---
TLSv1.2 with PFS disabled and insecure HTTP allowed
2016-05-25 21:00:14.052 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:14.064 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:14.088 nscurl[408:4734] CFNetwork SSLHandshake failed (-9824)
2016-05-25 21:00:14.112 nscurl[408:4734] CFNetwork SSLHandshake failed (-9824)
2016-05-25 21:00:14.113 nscurl[408:4734] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
Result : FAIL
---

---
TLSv1.1 with PFS disabled and insecure HTTP allowed
2016-05-25 21:00:14.128 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:14.144 nscurl[408:4734] CFNetwork SSLHandshake failed (-9801)
2016-05-25 21:00:14.185 nscurl[408:4734] CFNetwork SSLHandshake failed (-9824)
2016-05-25 21:00:14.211 nscurl[408:4734] CFNetwork SSLHandshake failed (-9824)
2016-05-25 21:00:14.212 nscurl[408:4734] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
Result : FAIL
---

---
TLSv1.0 with PFS disabled and insecure HTTP allowed
Result : PASS
---

================================================================================

ssl.conf 修正後(正常)パターン

Resultが、すべてPASSしていることが確認できるかと・・

Starting ATS Diagnostics

Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https://cdn.sslsitetesttest.com.
A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error.
Use '--verbose' to view the ATS dictionaries used and to display the error received in URLSession:task:didCompleteWithError:.
================================================================================

Default ATS Secure Connection
---
ATS Default Connection
Result : PASS
---

================================================================================

Allowing Arbitrary Loads

---
Allow All Loads
Result : PASS
---

================================================================================

Configuring TLS exceptions for cdn.sslsitetesttest.com

---
TLSv1.2
Result : PASS
---

---
TLSv1.1
Result : PASS
---

---
TLSv1.0
Result : PASS
---

================================================================================

Configuring PFS exceptions for cdn.sslsitetesttest.com

---
Disabling Perfect Forward Secrecy
Result : PASS
---

================================================================================

Configuring PFS exceptions and allowing insecure HTTP for cdn.sslsitetesttest.com

---
Disabling Perfect Forward Secrecy and Allowing Insecure HTTP
Result : PASS
---

================================================================================

Configuring TLS exceptions with PFS disabled for cdn.sslsitetesttest.com

---
TLSv1.2 with PFS disabled
Result : PASS
---

---
TLSv1.1 with PFS disabled
Result : PASS
---

---
TLSv1.0 with PFS disabled
Result : PASS
---

================================================================================

Configuring TLS exceptions with PFS disabled and insecure HTTP allowed for cdn.sslsitetesttest.com

---
TLSv1.2 with PFS disabled and insecure HTTP allowed
Result : PASS
---

---
TLSv1.1 with PFS disabled and insecure HTTP allowed
Result : PASS
---

---
TLSv1.0 with PFS disabled and insecure HTTP allowed
Result : PASS
---

================================================================================

恐るべし、iOS

あと、この件とは関係なのだけど、ssl.confで、 SSLv2 SSLv3を入れるのは、ぜい弱性の観点から入れないほうがいいらしいと言われたのですが、動かなくなるのが怖いので、そのままにしてしまっています