はじめに
タイトルまんまなんですが困っています。世界の人たちどうしてんの
こんばんはconvtoです。故あってgoogle sign inの仕様について調べてたんですが、表題通りの問題を見つけて頭を抱えています。
お客様の中に医師免許を持った有識者の方いらっしゃいましたら助けてください。
google sign inまわり、古いドキュメントも新しいドキュメントもいっぱいあって全体像があんまりつかめてないので僕が混乱してるだけかもだけど、だとしたらそれはそれで嬉しいのでとりあえず投下しておく
[追記]
インターネットから知見があつまりAndroidは大丈夫な雰囲気あります!記事の末尾に追記してます
iOSはまだnonce渡す手段見つかってないので引き続き調査中 & 有識者の意見お待ちしております
なにが問題なの
OIDCぽいのにnonceが指定できないから、session管理者がnonceの検証できなくてリプレイ攻撃の可能性がある
どゆこと
いわゆる「ほげほげでログイン」的なやつはたいていclient用のSDKが提供されがちですが、google sign inも例にもれずiOS/Android/JavaScriptのclientライブラリを提供してます。
(おまけにserversideのclient向けにもいくつかの言語で提供されてる)
丁寧にそれぞれのプラットフォーム向けの導入ドキュメントもある
- Android: https://developers.google.com/identity/sign-in/android/sign-in
- iOS: https://developers.google.com/identity/sign-in/ios/sign-in
で、なにが問題かっていうと、client SDKの作り的にnonce指定できないんすよねぇ。
clientとしてはnonceの検証はかなりやりたいんすよな
String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case-sensitive string.
一応そう判断したソースを貼っておくと
- Android SDKのほうは https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignInClient あたりを一通り見たけどnonce指定できるオーラなし
- ざっとしかみてないのでファントムの可能性あり
- iOS SDKのほうは こういうissue がでててどうやら渡せなさそう
- やりとりみるにiOSは依存ライブラリが勝手に内部で生成してる。外部から指定不可
- ライブラリ内でnonce検証されるっぽいけど、nonceの検証はsession管理者じゃないとやる意味ないはずなのでこのID Tokenをserverに渡してserverでログインさせる!みたいなフローだと効果なし
- 余談だけどJavaScript SDKではnonce指定できるっぽい
iOS/AndroidともにSDKではnonce指定不可、iOSは内部でnonce検証してるけどsession管理をserverでやるケースに置いては意味無し!という理解です。
ようはsession管理者をserverside側としたとき、iOS/Androidのclient SDKをつかうとsession管理者がnonceを知ることができないためsessionに紐づくnonceとID Tokenのnonce claimを突合できんわけです。
そもそもnonceは、sessionに紐付いたnonceを採番してわたすことでsession管理者がID Tokenのnonce claimと突合できて同一sessionで作られたID Tokenかわかる!からリプレイ攻撃されてもすでに正規loginでnonceが使われててもうないで〜とかいえるんでログイン弾けるやーつでして、まあそれがないと渡されたID Tokenをすべて全面的に一連の認証フローを経て正式に発行されたものと信頼するしかほかなくリプレイ攻撃のおもちゃになります。
なぜiOS/Androidのclient SDKで渡せないのか意味不明
iOS/Android SDKはclient以外にID Token渡すことを想定してないのかな?
そうか、そもそもclient以外にID Tokenを渡す想定してないんだな?発行したclientでのみの利用を想定してるなら、ついさっき自分で発行したばっかりだから信頼できそう?とおもったそこのあなた! 残念でした、serverとかにID Token渡してsession開始する使い方も想定してるようです
ご丁寧にID Tokenをserverにおくってserverのsessionを開始させてloginさせよう!という趣旨のドキュメントがあり、むしろそういう用途が主に想定されてるようです。ひょえ〜
- Android: https://developers.google.com/identity/sign-in/android/backend-auth
- iOS: https://developers.google.com/identity/sign-in/ios/backend-auth
つまり?
読み取った内容が正しければ、iOS/Androidのclient SDKを使うとsession管理者が管理するnonceを利用することができないため、リプレイ攻撃に対してなすすべがない。となります
有識者の意見を求めています
冒頭の通りお客様の中に医師免許を持った有識者の方いらっしゃいましたら助けてください。
nonceなくてもまあ一定は平気〜とかそういう安心できる情報お待ちしてます。まあなんかこういうのoptionalぽい扱いされがちなんだけどclient側ちゃんと検証しろよってMUSTで書かれてたのでええんだっけ?となった次第
google sign inの公式ドキュメント通りに実装したらリプレイ攻撃に対して対抗策がない!なんてことになってるなら世界のアプリのgoogle sign inはそこそこの割合リプレイ攻撃やれっぞということになるし、流石にそんなことない気はしている。
なんかmobile client文脈ならではの、例えば攻撃者がIdPから返される値を傍受することが何らかの理由で非常に難しいから基本的にリプレイ攻撃は考慮しなくていいんじゃよみたいな、安心できる理屈がほしいので有識者は是非twitter DMなりこの記事へのブコメなりでぼくを助けてください。twitterは https://twitter.com/convto です。
JS SDKとかを考えると裏側のWeb API自体はnonce対応してるんだろうからちゃちゃっと渡せるようにしてほしいな〜
感想
- 世界の人たちどうしてるの
- ぼくがmobile clientに疎いだけでmobileだとリプレイ攻撃への考慮不要だったりするんか?
- とおもったけど sign in with apple はちゃんとSDKからnonce渡せるやんけ〜〜 なんでgoogle sign inだと渡せないの〜〜
どちらにせよ普通にoptionalとしてnonce指定できたほうがいいと思うので、どっかにissue立てようかなぁ〜
追記
インターネットから知見が集まり、Android版はnonce渡せそう(または取得できそう)なことがわかった!
ぼくが見てたのは auth.api.signin
だったけど、どうやら新しく auth.api.identity
というやつがありそっちを使えば取得できるっぽい!
https://developers.google.com/android/reference/com/google/android/gms/auth/api/identity/GetSignInIntentRequest
全体的にGoogle Identity Serviceとかいう新しいsign inのサービス?がでてるっぽくそっちを使えばもろもろ整備されてるよう。
なんだけどiOSはやっぱ調べたライブラリ使ってて指定できない気がするんだよなぁ