Gilt: iOS7でbackground fetchを利用するとログアウトしてしまうバグへの対応

http://tech.gilt.com/post/67708037571/sleuthing-and-solving-the-user-logout-bug-on-ios-7

1 comment | 1 point | by WazanovaNews 約3年前


Jshiike 約3年前 | ▲upvoteする | link

Giltがエンジニアブログで、background fetchを利用するとログアウトしてしまうバグを修正した取り組みを紹介してます。


1) 背景

iOS7の新機能background fetchを利用すれば、裏側で定期的にサーバにリクエストをしてデータを取得できるので、ユーザに最新のコンテンツを提供できる。サーバ駆動型のアプリにありがちな、「読み込み中...」という表示も減らせる。毎日ユーザにセール情報を提供するGiltにとっては待望の機能。以前は、ユーザがアプリを立ち上げた後にサーバとの通信を繰返し、新しいデータを取得していたので、せっかくのフラッシュセールの目玉商品がやっと表示されたときには売り切れとなる事態もありえた。

2) An Unexpected Complication

iPhone 4.0のリリースにあわせてbackground fetchを導入。成果はすぐに現れたが、一方でバグレポートがくる。

ログインして、"Stay Signed In"を選択したユーザが、その後に突然ログアウトされる。再度ログインしても、数日後には再びログアウトされる。QAテストのプロセスでは気づかなかったが、自分たちの端末でも同じ事象がではじめた。しかし、何が原因なのかわからず、再現できなかった。いくつか推測でコードを修正をしてみたが、成果はでなかった。

3) A LaGuardia Eureka

その後、たまたまDelta航空のアプリを利用した際に、同じ事象のバグがあることに気づいた。他のアプリでも起きているということは、Giltの独自のコードに起因してるわけではないことが推定できた。これは、The VergeのEllis Hamburgerの記事でも裏付けられた。

4) The Source of Our Trouble

ログアウトのバグに関してやっかいだったのは、iOSのキーチェーンが常に取得できるわけではなかったこと。データがキーチェーンに保管されると、iOSはそれが安全に保管されたと見なすので、ロックコードでスクリーンがロックされていると、デフォルトでキーチェーンアイテムは暗号化されたままになる。指紋認証もしくはパスコードを入力して、デバイスをアンロックするまでは取得できない。これは、その端末を手に入れた他人がキーチェーンにあるセンシティブなデータにアクセスができないようにしている。

background fetchが採用されてからは、画面がロックされてキーチェーンにアクセスできない間も、アプリが実行されるということは起きる。具体的には、Giltのアプリは定期的に再認証し、ユーザのカートにあるアイテムをサーバと同期する仕組みになっている。これで、クロスでバイス間の適切な同期を実現していたのだが、この再認証が画面ロック中にバックグランドで実行されることがバグの原因であった。

このアクションがとられるときに、Gltのアプリはキーチェーンにユーザの証明書をリクエストするのだが、画面がロックしているため、キーチェーンはnullを返していた。証明書が取得できないと、ユーザがいないという判別になるので、次回のアクセスのときにはログアウトしている状態になっていた。QA端末には画面ロックのコードをセットしてなかったので、このバグのパターンには気づかなかった。

5) Don't Decrease Default Security

キーチェーンが原因だとわかってから、他の開発者がどのようなアプローチで対処しているのか調べてみた。kSecAttrAccessibleAlwaysにセットしたkSecAttrAccessible属性をキーチェーンに保管するというのが人気の解決方法であったが、セキュリティレベルが落ちる保管方法を使うというのがネックになる。

もし、アプリがbackground fetchのときに、正しく動作させる唯一の方法がセキュリティレベルを下げることであれば、コードのリファクタリングを検討すべき。kSecAttrAccessibleAlwaysをセットしてしまうと、デバイスがロックしたときもデータは暗号化されない。Giltはセンシティブなデータの扱い、及びセキュリティのレベルには相当慎重。

そこで、サーバへのリクエストモデルを変えて、認証はバックグランドで実行される必要がないようにした。つまり、キーチェーンが取得できなければ、シンプルに認証をリクエストしない。一度画面がロックされた後に、次にキーチェーンが取得可能になるタイミングがうまく取得できれば、認証およびカートの同期が、ユーザがアプリを見る前にできる。

6) Knowing When the Keychain is Ready

方法としては、UIApplicationDelegateのapplicationProtectedDataDidBecomeAvailable:メソッドにフックをしかける。

デバイスがアンロックされたときに、iOSがこのメソッドを呼び出す。アプリが画面に表示される前に呼び出せるので、先行して、キーチェーンから認証の証明書取得し、カートの同期ができる。

#gilt #モバイル #モバイルアプリ #iphone #ios #セキュリティ

Back