https://gist.github.com/mala/5062931
の続き。
Twitterの人に色々と問題点は伝えたんだけど、これからOAuthのサーバー書く人や、クライアント書く人が似たような問題を起こさないようにするために、どうすればいいのかについて簡単に書きます。既存の実装真似して作るとうっかりひどい目にあう。
自分は意図的に「Twitterの脆弱性」という表現を使わないように気を使っていて、それはクライアントアプリ側の責任もあるからなのだけれども、安全に実装するための方法がわかりにくかったり誤解を招きやすかったり、Twitterに買収されているTweetDeckにも問題があったりしたので、それはやっぱりTwitter側の責任の比重が大きいとは思う。とはいっても別に責任を追求したかったり◯◯はクソだといったことを言いたいわけではなく、誰が悪いとか言う以前に、複合的な要因によって問題が起きるときには原因を正しく理解する必要があると思う。
- サーバー側で秘密の鍵を持っている(それが無いとアクセストークンを取得できない or 使えない)
or
- 秘密の情報を持っていなくても事前に設定した https の callback URLに固定である
- callback URLが固定であれば、他人に渡ることが無い
- callback URLがカスタムURLスキームの場合は「悪意のあるアプリを入れて無ければ」という条件付きで安全である
- http://www.slideshare.net/nekoruri/20130301-twitter-oauthvulnerability
- http://shogo82148.github.com/blog/2012/11/24/no-more-webview/
ダミーのURLを登録しておいて、実際にはカスタムURLに飛ばすということを行なうアプリの話がでてきます。おいおい、それってconsumer secretは難読化しようが「漏れる」そしてアプリ設定で入力したCallback URLは無視されて、実際には自由に「いじれる」ということを、開発者が知ってるってことだ。
大事なことなのでもう一度言いますよ。「秘密の情報もない」し「callback URLを強制」することもできていない。偽のクライアントを作ることが出来て、callback先は自由に設定できる。何でこれで安全に実装できると思っているんだ!! ギルティ!!
consumer secretが漏れているクライアントアプリがあって、その際にtokenを受け渡す先が自由に設定できて、かつ「自動でリダイレクトする」という状況だったわけです。現在「自動でリダイレクトする部分」が塞がりました。その部分は塞がりましたが、consumer secretを隠せない以上は、
- 偽のアプリ、サイトでも、正規のアプリケーションの認可画面を表示することが出来て、許可ボタンを押すと誰にトークンが渡るのか分からない
という状況に変わりは無いです。これを防ぎたかったら任意のURLへのcallbackをやめることです。callbackせずにPINコード入力するか、ユーザーがアプリ信用してパスワード直接入力するか、OSが管理する特別なクライアントから又貸ししてもらうという方法があります。Twitterはそういう方式を用意していたわけですが、実際にはカスタムURLスキームにリダイレクト + consumer secretも当然隠せてない + リダイレクト先自由に設定できる、というアプリが大量に出来てしまった。
任意の http://* へのリダイレクトでconsumer secretが漏れてるなら当然に危険なわけだけど、じゃあ、カスタムURLスキームへのリダイレクトならどうなのか。
あなたの開発する何らかのアプリが mygreatapp:// をハンドリングしているとして。あなたは mygreatapp:// を取得するためにお金を払いましたか?払ってないと思います。AppleやGoogleにお金を払っていることはあるかもしれませんがそれは mygreatapp:// 取得料金ではないはずです。もし払った覚えがあるならばそれは詐欺なので高いツボを買わないように注意しましょう。callback先に mygreatapp:// を指定したとしても、それはあなたのアプリケーションに対してリダイレクトしてくれることは保証できないのです。別のアプリが既に使っていたり意図的にかぶらせる可能性があります。Twitterとしては安全でないことを知っているのでカスタムURLスキームを事前登録のcallbackには指定できないようにしていたのかもしれません、その結果ダミーのURLが登録されるようになってしまった。
現実的な問題としては、consumer key/secretをコピーして使っているアプリ、OAuth2.0だったらclient_idをコピーして使っているアプリ、があったなら、それは偽アプリで、マルウェアである。そんなものが偶然に一致することはない。アプリ側の設定でランダムで長いカスタムURLスキームを設定させて、callback先を固定するというのであれば、それが「偶然に一致」することはない。偽アプリの配布はAppleやGoogleが防いで「正規のアプリかどうか確認しましょう」「意図しないタイミングでいきなり認可画面が表示されたら気をつけましょう」という「運用でカバー」ができてしまう。callback先をアプリ毎のカスタムURLスキームに固定、は問題を「軽減」します。ユーザーが悪意のあるアプリを事前に入れておかなければ、攻撃が成立しなくなるからです。
運用でカバーできてしまうがゆえに、PINコード入力ダサいと考える開発者がカスタムURLスキームへのcallbackを使う。皆平然と使っているように見えますが、別に安全であると保証されているわけではないです。ブラウザから外部アプリ勝手に開くのか、確認があるのか、ハンドリングするアプリが複数定義されている場合にはどうなるのか、そういったことは完全にOS依存です。
新規で作るなら以下のようにすると良いと思います。
- 認可画面で勝手にボタンを押せないような対策は当然する
- 事前登録されたcallback URLが http で始まるなら、それは完全固定であるべきだ。
- カスタムURLスキームに、少なくとも「自動で」リダイレクトしてはいけない。誰がハンドリングするのか保証されていない。
固定のcallback URLを担保にして安全性を保証したい場合に「ドメインが一致していればOK」や「前方一致」は避けるべきです。同一ドメイン内にリダイレクタや、外部に表示中のURLを漏らしてしまう機能(アクセス解析等)が多分あります。高い確率であります。これはOAuth1.0でも2.0でもOpenID Connectでもオレオレプロトコルでも共通して言えることですが「秘密の情報を持ってもいないし、callback先も自由」という状況を作ってはいけない。それから「カスタムURLスキームは誰がハンドリングするのか保証されているわけではない」ということを覚えておけば良いと思います。
Twitterが今すぐに「事前登録されたcallback URLに固定」ということをやってしまうと、いくつかのアプリが死にます。事前登録したCallback URLと、oauth_callbackの値が一致しないということは、それはconsumer secretが漏洩しているアプリケーションに対する攻撃である可能性があるということですが、それをブロックすると「カスタムURLスキームに飛ばすためにダミーのURLを登録してる」「正規のアプリ」も使えなくなってしまう、という状況になってしまっている。なんたることだ!! おい誰だよ、ダミーのURLを登録するなんてライフハックを広めた奴は!!
- サーバー側は勝手にリダイレクトしちゃうの良くないしcallback URL事前登録するならそれは固定であるべきだし、秘密の情報持てないクライアント向けの方式をサポートする必要がある
- 何を担保にして安全性を保証しているのかを意識する必要がある
- クライアント側はサービス運営者側の推奨する方式を使いましょう