ラベル pokemon の投稿を表示しています。 すべての投稿を表示
ラベル pokemon の投稿を表示しています。 すべての投稿を表示

2016年8月3日水曜日

安全!? #PokemonGo のログインと OpenID Connect と PokeIV

こんにちは、富士榮です。

今回はもPokemon Goとアイデンティティのネタです。


前回、前々回とPokemon Goのログインの流れを見てきました。

 攻略! #PokemonGo のログインと OpenID Connect
 http://idmlab.eidentity.jp/2016/07/pokemongo-openid-connect.html

 続!#PokemonGo のログインと OpenID Connect
 http://idmlab.eidentity.jp/2016/07/pokemongo-openid-connect_25.html


これまでのポストでは、主にGoogleでログインすることで、Googleサービスへの不要なアクセスができる状態になっていないか?という観点で調査をしてきましたので、Scopeパラメータを見る限りはメールアドレスくらいにしかアクセスしていないことが分かったので、ひとまず安心、という結論を出していました。


◆Pokemon Goのふりをするアプリの登場

しかし、逆にPokemon Go側から見ると、Pokemon GoのサービスへのアクセスはGoogleから取得したid_tokenを持っていることだけを条件にしているので、何とかしてPokemon Goアプリの”ふり”をしてid_tokenを取得してしまえば、あとは何とでもなる、という状態でもありました。

最初のポストを書く時に、OpenID ConnectでGoogleからid_tokenを取得する流れを見ていると、public clientにもかかわらずimplicitではなくcode flowを使っており、codeやclient_secretが丸見えな点にものすごく違和感を感じて、サードパーティ・クライアントでも作るつもりなのかな???など、色々と議論をしていたんですが、やはり出てきてしまいました。

 PokeIV : 個体値チェックサイト
  https://pokeiv.net/



Gigazineでも紹介されていますが、Pokemon Goで使っているアカウントでGoogleにログオンし、codeを取得しPokeIVに貼り付けることでPokemon Goへアクセスして個体情報を取得するという仕組みなのですが、内部的には、Pokemon Goのclient_idとclient_secretを使ってPokemon Goのサービスへアクセスできるid_tokenを取得しているようです。

以下の同意画面を見るとクライアントがPokemon Goになっており、クエリパラメータの中のclient_idを見るとiOS版のPokemon Goで使っているclient_idと同じものが使われています。


Googleに登録されているredirect_uriがurn:ietf:wg:oauth:2.0:oobなので、redirect_uriにクエリでcodeを渡す代わりに、取得したcodeを手動(コピペ)で渡し、tokenエンドポイントを叩く、という実装はしていますが、色々とタチが悪いですね。

最近OpenID Foundation Japanの事務局長になったnov氏がoauth.jpで書いているようにcode置き換えもあるでしょうし、そもそもclient_secretが漏れている段階で今回のPokeIVのように単純にPokemon Goのふりをしてデータを取得するようなクライアントの開発が出来てしまいます。

今は集めたポケモンの情報をとってくるだけ、ということになっているので良いんでしょうが、例えば、今後Pokemon Goからユーザの位置情報の履歴を取得できたりすると、一気にホラーな話になってしまいますよね。。。


◆Pokemon Goはどうすれば良かったのか?

そもそも論、HTTPSを使っているにも関わらずFiddlerでトレースできてしまう段階でCertificate Pinningなどの対策が出来ていない、という話もありますが、ネイティブアプリ(パブリック・クライアント)として実装している段階で逆コンパイルや解析をされる前提でOpenID ConnectやOAuthの設計をすべきです。具体的にはimplicit flowを使ったり、Google Sign-In SDK for iOSを使って、client_secretを使わない方法で実装するのが一番簡単な対策だと思います。

簡単にまとめるとこんな感じかと。(他にもあるとは思いますが)

脅威対策
通信トレースによるcodeやclient_secretの漏えいCertificate Pinning
バイナリ解析によるclient_secretの漏えい難読化(限界あり)
Proxyサーバの通信ログよりcodeの漏えいPKCEの利用(Googleは既にPKCE対応してます)
client_secretの漏えいによるクライアントなりすましclient_secretを使わないフローの採用(implicit/Google Sign-In SDKの利用)



◆現バージョンのPokemon Goはどうなっているのか?

最近バージョンが上がったクライアントはどうなっているのか?を見てみました。

Proxyを挟んだ状態でアクセスするURLなどを見て推測すると、Google Sign-In SDKを使っているみたいです。また間にfiddlerを挟んだ構成だとログオンに成功しないので、MITMもチェックもしているみたいです。(うまくトレースできなかったので深追いはしていませんが)


◆PokeIVなどのアプリを使ってしまった場合の対策

しかし、一度PokeIVなどのアプリを使ってしまった場合、id_tokenやrefresh_tokenがアプリ側に保管されている可能性もあり、勝手にPokemon Goへ不正にアクセスされてしまう可能性もゼロではありません。

このような場合は、Googleアカウントのページからアプリケーションからのアクセスを一旦取り消して、正規のPokemon Goで再度サインインをしましょう。

アクセスを許可しているアプリケーション一覧が以下のURLで確認できるので、Pokemon Goを探して、一旦削除してしまいましょう。

https://security.google.com/settings/security/permissions



Pokemon Goを起動すると再度Googleアカウントへのアクセス許可のページが表示されますので、ここで改めて許可をすることでid_tokenが取得できるので引き続きPokemon Goを楽しむことが出来ます。

2016年7月25日月曜日

続!#PokemonGo のログインと OpenID Connect

こんにちは、富士榮です。

昨日に引き続き Pokemon Go / ポケモン Go です。

だいぶレベルあがりました。


昨日のポストに対する皆様の反応を拝見していると、iOS版はわかったから、Android版はどうなの?という意見が多かったので、今回はAndroid版です。

また、結局 Pokemon Go はどういう権限を要求しているのか?について若干混乱が見られたので、そのあたりもついでに整理していこうと思います。

では、早速見ていきます。

まずは、Android版の動きです。

◆環境設定

昨日のiOSと同様に、Wifi設定よりProxyサーバとしてPC上で動かしているFiddlerを使うように設定します。


準備はほぼ同じなので、さっそくGoogleでログインしてみます。


◆ログイン時のトラフィックの違い

実際にログオンしてみるとiOSとは大きな違いがあることがわかります。
iOS版では、WebViewが立ち上がりGoogleアカウントでログインする必要がありますが、Android版ではOSにログオンしているGoogleアカウントがシームレスに使われるため、改めてGoogleへログインする必要がありません。

トラフィックをキャプチャすると、android.clients.google.com/authに対して直接client_idなどをPOSTしてid_tokenを受け取っています。

POST https://android.clients.google.com/auth HTTP/1.1

POSTデータ
androidId=3798d3c8e02a1cce
&lang=en_US
&google_play_services_version=9256438
&sdk_version=23
&device_country=jp
&client_sig=321187995bc7cdc2b5fc91b11a96e2baa8602c62
&callerSig=321187995bc7cdc2b5fc91b11a96e2baa8602c62
&Email=reign.of.pharaoh%40gmail.com
&service=audience%3Aserver%3Aclient_id%3A82..snip..b.apps.googleusercontent.com
&app=com.nianticlabs.pokemongo
&check_email=1
&token_request_options=
&callerPkg=com.nianticlabs.pokemongo
&Token=oauth2rt_1%2FBF_GAY86cPwrwUgpf2HZd3pV71FRTE0q-YgCePsVp2E

ちなみに、iOS版とはclient_idが違うんですね。
このリクエストに対して、HTTP 200で以下のデータが返ってきます。Authにid_tokenが入ってきています。

Auth=eyJhbGciOiJSUzI1N...snip...d9LC7FGaQ
issueAdvice=auto
Expiry=1469374236
storeConsentRemotely=1
isTokenSnowballed=0

jwt.ioでdecodeするとちゃんとid_tokenになっています。



以降のAPIコール時はiOSと同じくid_tokenがPOSTされているので、Android版でもid_tokenに乗っている情報以外(iOSと違って、メールアドレス以外に姓と名がid_tokenに含まれます)はサービスにはわたっていないことがわかります。

まぁ、一安心ということで。



◆結局どういうパーミッションが求められているのか?

前回も今回もOpenID Connectという文脈でPokemon GoアプリケーションがGoogleへどのような情報や操作を要求しているのか?を見て、とりあえず新規にGoogleアカウントを作らなくてもよさそう、という結論に達しました。

しかし、前回のポストに対するコメントとして、連絡先へのアクセスが要求されたりするので、やっぱりアカウントは新規に作る方がいいのでは?みたいなコメントもありました。

確かにAndroid版を見ると、アプリが端末に対して連絡先へのアクセスを要求しています。
※理由はよくわかりませんが。。。
<iOS版>

<Android版>


ここは非常に混乱を招きやすい部分ですが、結論から言いますとアプリケーションが端末に対して要求している権限と、Googleアカウントでログインする際にGoogleに対して要求する権限は全く別のものであり、Googleアカウントの新規作成うんぬん、という話は後者にしか関係しません。(いわゆる、OAuthのscopeパラメータの話ですので)

ここで言う連絡先へのアクセスは前者の端末インストール時の話なので、連絡先へのアクセスが嫌なのであれば、Googleアカウントを新規に取り直すのではなく、専用端末を新規に購入すべきである、ということになります。

まぁ、この辺りは確かにユーザにとってわかりにくいですね。


2016年7月24日日曜日

攻略! #PokemonGo のログインと OpenID Connect

こんにちは、富士榮です。

世の中 Pokemon Go / ポケモン Go 一色ですが、いかがお過ごしでしょうか?

あいにく私の住んでいる地域は過疎化が進んでおり、ポケモンがほとんどおりません・・・・



おかげで全然レベルが上がらないので、ジムなどには無縁な平和な生活を営んでいるわけなんですが、果たして Pokemon Go のログオンは平和なのか?という ID 厨なら必ず抱く疑問を解消すべく、家族で地元の夏祭りに参加している横で解析をしてみました。

公開当初はフルアクセスを要求するアプリだったので、プチ炎上したりしましたが現状は解消されたということなので、パケットキャプチャしてみます。

 参考)ポケモン Go がフルアクセスを要求していた件。



では早速、Pokemon Go へのログインの仕組みを見ていきましょう。
Pokemon Go ではいわゆるローカルアカウント(Pokemon Trainer Club)と Google アカウントを使ってログオンする2つの仕組みが用意されています。

色々な Web サイトを見ると、専用の Google アカウントを作るべきである、というような記事を見かけたりしますが、根拠として先のとおり、すべて権限を要求するから、という理由が挙げられています。

 参考)ポケモン Go をプレイするなら専用の Google アカウントを作るべき


まぁ、公式発表によると権限周りは修正されたということなのですが、実際にどのような権限が要求されているのか、ログオン~ユーザ登録のシーケンスにおいて何が起きているのかを見ておきたい、ということで解析してみましょう。

◆準備

基本的に通信をキャプチャすることになるので、プロキシを用意して HTTP/HTTPS の通信を覗いてみたいと思います。今回はiOS版を使ったので、PC にインストールした Fiddler をプロキシとして使うように iOS の Wifi 設定を行います。

今回は、PC で Fiddler を立ち上げ、リモートクライアント向けのプロキシとして動作するように設定を行いました。

Tools より Fiddler Options を開き、Connections タブへ以下の通り設定を行います。
- Fiddler Listen Port : 8888(デフォルト)
- Allow remote computers to connect : ON

これで PC にインストールした Fiddler がプロキシとして動作するようになります。
ちなみに、 Windows Firewall が邪魔をするので、外部からの 8888 への通信を許可するように設定をしておく必要があります。

また、HTTPS 通信を復号するためには、Fiddler の使う証明書を iOS が信頼するように構成する必要があるので、 Fiddler で証明書をエクスポートし、iOS 側へ送信、インポートしておく必要があります。(細かい手順は割愛します)


次は、iOS 側の設定で、Fiddler をプロキシとして使うようにしまう。
設定から Wifi を開いてプロキシサーバのアドレスとポートに Fiddler を動かしている PC のアドレスと先のポート番号を指定します。




これで準備は完了です。

早速、Pokemon Go をインストールしてサインアップしてみます。

◆Google アカウントでサインアップした場合の要求スコープは?

Pokemon Go を起動し Google アカウントでサインアップを選択します。


ここで、Googleを選択した時の通信キャプチャを見ると、以下のようなリクエストが飛んでいます。


GET https://accounts.google.com/o/oauth2/auth
?client_id=84....snip......apps.googleusercontent.com
&redirect_uri=urn:ietf:wg:oauth:2.0:oob
&response_type=code
&scope=openid email https://www.googleapis.com/auth/userinfo.email HTTP/1.1

注目すべきは scope パラメータです。OpenID Connect のリクエストなので当然 openid は含まれるとして、email および https://www.googleapis.com/auth/userinfo.email が含まれています。
Google の OpenID Connect 関係のドキュメントを見ると、email と は同じスコープで、メールアドレスの表示だけを要求していることがわかります。

画面上に表示されるとおりですね。

ここで、許可をクリックすると token エンドポイントへ認可コードが POST される普通の OpenID Connect の流れが続きます。

POST https://accounts.google.com/o/oauth2/token HTTP/1.1

ボディ
client_id=84...snip...alem.apps.googleusercontent.com
&client_secret=NCjF1...snip....uL7
&code=4/D7vS...snip....i7h1Q_njLCM
&grant_type=authorization_code
&redirect_uri=urn:ietf:wg:oauth:2.0:oob
&scope=openid email https://www.googleapis.com/auth/userinfo.email


結果、access_tokenやid_tokenが返ってきます。

{
  "access_token" : "ya29.Ci8pAxbo....snip...FA",
  "token_type" : "Bearer",
  "expires_in" : 3600,
  "id_token" : "eyJhbGciOiJSUzI1....snip.....XsXHNwQ",
  "refresh_token" : "1/lCAJhP...snip....TM"
}


もちろん id_token の中身を jwt.io などで覗くといい感じで Google アカウントの情報が出てきます。




さて、問題はこの後です。
email をスコープとして access_token を要求しているので、必要以上の情報を Google が提供することはありませんが、どのような要求が Pokemon Go から実施されているのでしょうか?

結論、サインアッププロセスの中で access_token が使われることはなく、 id_token の中のメールアドレスだけを使っているように見えます。

続くリクエストは運営元の Nianteclabs.com への POST なのですが、所謂 RESTful API 的に JSON などがやり取りされるわけではなく、バイナリデータが飛び交うのでこれ以上の解析はしませんが、見えている範囲では、 id_token がそのまま POST されているようです。

POST https://pgorelease.nianticlabs.com/plfe/rpc HTTP/1.1

ボディ
<いろんなバイナリ情報>
google
   eyJhbGciO....snip.... <- div="" id_token="" post="">


と、いうことでやっぱり OpenID Connect でのログインおよびメールアドレス情報だけを渡している、ということで、まぁ大丈夫そうです。


◆まとめ

今回はあくまでサインアッププロセスのみを見ているので、続く処理で何が行われているかはわかりませんが、とりあえずという意味では以下のことが言えると思います。

  • Pokemon Go のログイン・サインアップには OpenID Connect が採用されている
  • email アドレスのみを読み取る Scope で access_token が要求されているが、実際は id_token だけが使われている
  • 結果、メールアドレスくらいしか連携されていなさそうなので、別アカウントを作ったりする程にセンシティブにならなくてもよさそう?


注記)
  • 解析はあくまで一部分のみを対象としているので、他の処理で色々と情報を収集している可能性があります
  • 本解析は Pokemon Go のサービスを攻撃することを意図したものではありません
  • 本ポストが Pokemon Go のサービス規約に違反するなどし、サービス提供者の意図にそぐわない場合は本ポストを削除する可能性があります