iOSでの端末固有識別子の話。
iPhone - UDID確認アプリが全くアテにならない件 - Qiita
というのを見かけて、なんとなくまとめてみたくなりました。
Appleは端末を固有識別しようとする試みを挫こうとします。ただそれは 横断的に固有識別を行おうとしている ケースだけであって、UUID(Universally Unique IDentifier)の生成・利用に対しては基本的に寛容です。
UDID (Unique Device IDentifier)
AppleのiOS製品にはUDIDという概念があり、端末毎に一意の識別子が振られています。
[[UIDevice currentDevice] uniqueIdentifier];
によって取得ができましたが、iOS6からは利用するとリジェクト対象となり、iOS7からは返却される値がUDIDではなくなり、FFFFFFFF
とidentifierForVendor
(後述)によって連結される文字列に変更されました。
いくつか疑問が生まれると思います。
「なぜリジェクト対象となるメソッドを用いたアプリが未だに存在するのか?」
これはiOS5以前に申請され、その後開発者ライセンスの更新のみ行い、アップデートを行わないアプリが存在するからです。
レビュープロセスは申請時のみに行われるので、放置されたアプリはそのままです。
「なぜリジェクト対象のメソッドの、返却値を変更する必要があるのか?」
これは前述の理由と、それに加えてiOSアプリには審査を経由するコンシューマー向けアプリだけではなく、B2B向けのストア外のアプリが存在するからだと思います。
「そもそもなぜUDIDを使うべきではないのか?」
UDIDは強力な固有識別子ですが、通信を偽装して別のUDIDに成りすますことは非常に容易です。
UDIDを端末の識別子として認証等に利用することは、アプリ内IDの乗っ取り・個人情報漏洩などの脆弱性に繋がります。しかも一度流出したUDIDを変更する術はありません。
にも関わらず、濫用されすぎました。
IMEI (International Mobile Equipment Identity)
IMEI(国際移動体装置識別番号)は国際規格で、携帯電話端末に対して付与される、本来は一意な識別子です。
厳密な定義はややこしいので割愛すると、おおよそは「携帯電話に対して製造時に割り当てられる固有のID」と考えておけば問題ないです。その性質から、タブレット端末などには割り当てられていないことが多いです。
「本来は一意」というのは書き換えることが不可能ではないためです。通信事業者がネットワーク利用制限(いわゆる赤ロム)をIMEIによって行っているため、書き換えるべきではありません。国によっては法律で罰せられます。
iOS3までは取得する方法が存在しましたが、iOS4で塞がれており、現在は取得する方法は見つかっていないはずです。
MACアドレス
データリンク層(OSI参照モデルの第3層)にて、ノードの識別を行うための識別子です。
基本的にハードウェア毎に固有の識別子が割り当てられているはずなのだけど、しばしば重複するという話を聞きます。あくまでLANレベルでの固有識別を目的としたものなので、ハードウェアの設定で書き換えることができますし、厳密な固有識別子に使うには緩い感じです。
にも関わらず、「UDIDがダメならMACアドレスだ」と考えた人が一定層いたらしいです。
iOS6までは取得することができましたが、iOS7では固定値を返却するように修正されたため、現在では取得できません。
3rd-Party Cookie
3rd-Party Cookieとは、ページ内に表示されている広告など、現在見ているサイトとは異なるドメインからCookieの読み書きを行うことです。
一部のアドネットワークはこの仕組みを利用し、アプリ起動後一瞬Safariを立ち上げて、Cookieに書き込みを行い、そこからURLスキームでアプリに戻す(この手法はSafari Flash
だとかSafari Flip-Flop
だとか言われてたらしいですが、寡聞にして聞かなかったです)ことで、ユーザーのアプリ利用状況や嗜好を把握しようと試みました。
1年ほど前、この手法に対してリジェクトを行い始めた…という話を聞いたのですが、その後どうなったのかは良く分かってません。
3rd-Party CookieはiOSに限らずしばしば問題になりますが、CookieそのものはステートレスなHTTP通信において、安全なデータのやり取りを行うためのセッション確立に利用される有用な技術です。
影響範囲
iOSのSafariでは3rd-Party Cookieの拒否はできないので、Safariを普段使いしている場合、ここで仕込まれたcookieによって表示される広告が変化したり、インストールされているアプリを類推される可能性があります。しかしcookieの削除は可能ですので、永続的な識別子にはなりえません。
他のブラウザを利用している場合は、影響がないと思います。
UIWebViewでのcookieについて
アプリ内部のUIWebView
はSafariとはcookieストレージが独立しているので、アプリ内部のブラウザでcookieの読み書きを行われても特に悪影響はありません。
APNs(Apple Push Notification Service)のdeviceToken
Apple Push Notification Service、いわゆるPush通知では、サードパーティサーバが通知対象となる端末を識別するために、トークンを利用しています。
このdeviceToken
は、iOS6までは(多分)プロビジョニングプロファイル単位で同一の値が取得されていました。
「deviceToken
を端末固有識別に利用しよう」という発想の人がいたらしいです。色々考える人がいるものですね。
このため、Push通知を実装していないのにdeviceToken
を取得するとリジェクトされるようになり、iOS7では抜本的な対策が行われ、インストールされるアプリ毎に異なるdeviceToken
が割り当てられるようになりました。
Identifier For Vendor
廃止されたUDIDに替わる標準の端末識別子としてAppleが利用を推奨しているものです。
[[UIDevice currentDevice] identifierForVendor];
で取得できます。
Bundle Identifier
ごとにユニークな値が取得されるのですが、iOS6と7で仕様が違います。
例えばcom.hoge.fuga.app
というBundle Identifier
の場合、iOS6ではcom.hoge
単位で、iOS7ではcom.hoge.fuga
単位で生成される値が変わります。
一定の条件でiOS6.0にアップデートした端末では取得する値にバグがありました。このメソッドで取得した値を00000000-0000-0000-0000-000000000000
と比較するコードがあったらその名残です。現在ではもう不要だと思いますけども…。
Advertising Identifier
AdSupport.framework
に含まれるもので、同じくUDIDに替わる標準の端末識別子としてAppleが利用を推奨しているものです。
UDIDやidentifierForVendor
との違いですが、
- 端末毎に固有の値が生成される
- ユーザーによって利用をオプトアウトできる
- ユーザーの操作によって値がリセットされる
ことです。
UDIDと比べると安全ですが、それでも限りなく近い性質を持つことから、利用目的は厳しく規定されているようで、広告以外の用途で利用している場合はリジェクトされます。
これも一部のiOS6.0でバグってました。
UUID (Universally Unique Identifier)
ソフトウェアを一意に識別するIDです。
標準規格を読んで暗号論的擬似乱数生成器を実装しなくても、メソッド2つで生成できます。
端末固有識別子としてUUIDを扱いたい場合、初回起動時に[[NSUUID UUID] UUIDString]
(要iOS6、iOS5の場合は要CoreFoundation操作)で生成した文字列をキーチェーンに保存してください。
キーチェーン操作はC言語でやる必要がありますが、Objective-Cラッパーも沢山あります。利用用途に合わせて探してもいいですし、保存と読み出しのAPIだけであれば簡単なので、独自実装してもいいです。ただし深遠を覗き込むと死にます。
複数のアプリでUUIDを共有する(それはもうUUIDとは呼ばない気がするけれど…)こともできます。この場合、プロビジョニングとBundle Identifier
を統一した上で、KeychainGroupを指定する必要があります。またリストア時にデータのバックアップ対象とするかなどの設定項目もあります。手を抜いたラッパーライブラリはこの辺端折ってるので注意です。