Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

OSSのクライアントインストール型プログラム(Atom用パッケージ)でOAuth2をなるべく安全に利用する

More than 5 years have passed since last update.

はじめに

前の記事の、「配布物にOAuth2で利用するclient_secretを含むことのリスク」 についての続き。

いろいろなケースでも使えるとは思うが、具体的にはAtom用パッケージでGoogleのAPIを利用するためにOAuth2での認証を行う方法になっている。

先日のポストのあと、Twitterでのコメントも合わせて、良く分かってないながらもいろいろ整理してみた。ここらへんが結構わかりやすかった。 → 「RFCとなった「OAuth 2.0」――その要点は?

まず、公開するプログラムのタイプは2つありそう。

  1. サーバー型のプログラムで利用する前に前準備が必要で対象は不特定多数
  2. クライアント側のプログラムで利用者はほぼ本人のみですぐに使いたい

また、公開方法についても2つある。

  1. バイナリで公開
  2. ソースで公開

今回のパターンはAtomのパッケージなので、2-2に該当するパターンとなる。
例えば、1-2であればサーバー側の処理次第で client_idredirect_uri も隠せるだろうし、バイナリであれば中に隠せる(本当に悪用するのが目的の場合は隠し通すのは難しいと思われるが)と思うのであまり問題にはならなそう。

なので、ここではソースで公開するクライアント側のプログラムに的を絞って考えてみることにする。

目指すポリシーとそれに対応する望ましいフロー

ポリシー

ポリシーについては

  1. クライアント側で個人が利用するプログラムなのでできるだけ面倒なことは避けさせたい
  2. とは言っても認証と認可は必要なのでOAuth2を利用する(API_KEYは使えない)
  3. client_secret.json はそのままの形で悪用される可能性が強いのでソース同梱は避ける
  4. いざというときのために client_id を変えても利用者にはあまり影響がないようにする
  5. 認可の際に確認はできるものの client_id が悪用された場合のことを考えて scope は変更できないようにする
  6. 取得した access_token についてはメモリ内だけで処理してファイルやlocalStorageに残さないようにする

あたりが妥当なところだろうか。

フロー

これを満たすためには、

  1. Atomの起動時にAtom経由でネット上の認証準備用ページを開く
  2. 認証準備用ページでは予めセットしておいた redirect_uriscope を使ってGoogleの認証画面を開く AtomとGoogleの組み合わせだと一度認証と認可が行われていればユーザーの操作なしに access_token が新規発行されるのでその場合は6へ進む
  3. Googleの認証画面でIDとパスワードを入力して認証作業
  4. Googleの認可画面で利用するプログラム名と scope の範囲を確認して認可する
  5. 予めセットしておいた redirect_uri へ転送される
  6. webviewの機能を利用して access_token を取得する
  7. access_token は保存しないで変数を使いまわして期限(3600sec)が来たら1に戻って再取得する

という流れを作れればなんとかクリアできそう。

実装内容

上記のポリシーとフローを意識しながら、AtomパッケージでOAuth2認証・認可を行い、取得した access_token を用いてAPIを利用するまでをまとめてみる。

GoogleAPIの設定

まずはフローに合わせた新しい認証情報を作る

  1. GoogleDevelopersConsole で新しいプロジェクトを作成
  2. 利用したいAPIをオンにして「ウェブアプリケーション」タイプのクライアントIDを作成する
  3. リダイレクトURIについては、この次に説明するGitHubPagesのアドレスを入れておく
    例: http://USER_ID.github.io/PACKAGE_NAME/oauth2callback

ちなみに、今回作成した client_secret は使わない感じ。

GitHubPagesの作成

初めはGoogleAppsScriptでお手軽に作ろうと目論んでいたが、Googleの認証画面へのリダイレクトっぽい動作を実装できずに断念。
結局はGitHubPagesで 実装 してみた。結果的にだが、ブランチ違いとはいえ1つのリポジトリで全てが見えるので透明性の確保という点でも良い選択だったと思う。

ソースを見てもらうのが一番早いが、一応簡単に説明すると

  1. GitHubでgh_pagesブランチを作成(GitHubのページ作成から実行すると楽)
  2. oauth2callback.html を作成(名前はなんでもいいんだろうけど、この名前が一般的っぽいので)
  3. ここ を参考にしてHTMLを作成

という感じ。

上記だけであればWebブラウザ上だけで行える。ファイルとかディレクトリとかまですべてキレイにしたい場合はGitクライアントでの操作が必要だけど。

そして oauth2callback.html の中身については、内部的にはGoogle認証ページヘの誘導部分とGoogle認証ページからの戻りの部分に分かれている。
Google認証ページへの誘導部分は、任意のURLのハッシュ(今回は #auth )の時に、GoogleAPIに登録した client_idredirect_uri 、その後に利用する scope をつけて、Googleの認証ページヘリダイレクトさせてる。これで client_id をパクられてもスコープは固定に出来る。
他にGoogleAnalysticsのスクリプトもつけているが、ここらへんはお好みで。

次にGoogle認証ページからの戻りの部分では、単に認証が成功したとのメッセージを表示させるだけ。
URLのハッシュ部分に access_token がついているのでここでは何もしなくとも良い。

この段階で redirect_uri#auth にアクセスすると認証画面が表示され、IDとPASSWDを入れると認可画面が出て、許可した後に 「Authorization succeeded for PACKAGE_NAME.」 が表示された画面に遷移すれば認証・認可フローは完成となる。

Atomパッケージへログインページの実装

(これ以降はAtomパッケージに限った話になります)

あとは、認証準備用ページのリダイレクト用JSが処理できて、各種HTMLを処理できて、最後はGoogleから送られてくるURLハッシュを取得できるログインページを実装する必要がある。
そういうことが出来るのか探したところ、AtomというかElectronには(にも?) webview というカスタムタグがあることを発見(これを発見したからこのページの実装を思いついたわけだが)。

詳細は The tag を見ていただくとして、外部から表示させるURLを送ったり、ロード後の状態を取得したりも出来る。

これを元にしてログインページを実装したのが、 atom-gmail-checker-auth-view.coffee になる。
ちょっとCloseボタンの表示がイケてないが、これくらいの実装でも今回のフローにあった内容の処理が実現できる。

そしてこれをログインの時に表示させればいいのだが、今回は右側に新しいパネルを作成してそこに表示させている。
本当はモダールで表示させたかったのだがテーマで幅が決まってるらしく、ハックが面倒なので諦めた。

また、 access_token の他に Expires_in も取り出し、タイマーかけて access_token は再取得している。
ここらへんはReflesh_tokenがどうのこうのとかありそうだけど、 Using OAuth 2.0 for Client-side Applications
を見ても reflesh_token なる文字が出てこないので、これはこういうものだと思って都度取得にしている。

あとは取得した access_token は変数だけで利用するようにしてAPIを叩けば、client_secret.json 無しで無事にデータが取得できる。

ここで実装したログインページはこんな感じになる。
atom-oauth2.gif

ポリシーの検証

ここでポリシーを検証してみる。

1. クライアント側で個人が利用するプログラムなのでできるだけ面倒なことは避けさせたい

利用者に client_secret.jsonclient_id を用意させること無く、表示された認証画面で認証し、認可画面で許可ボタンをクリックするするだけで利用できている。

2. とは言っても認証と認可は必要なのでOAuth2を利用する(API_KEYは使えない)

API_KEYなどは利用しないで、OAuth2を利用している。

3. client_secret.json はそのままの形で悪用される可能性が強いのでソース同梱は避ける

client_secret.json は利用せず、サーバー側に設定。

4. いざというときのために client_id を変えても利用者にはあまり影響がないようにする

もし client_id を変更するようなことがあっても、GitHubPagesの設定を変更するだけでOK。

5. 認可の際に確認はできるものの client_id が悪用された場合のことを考えてSCOPEは変更できないようにする

client_id に紐づく redirect_uri のサーバー側で設定されているので、SCOPEを変更することは出来ない。

6. 取得した access_token についてはメモリ内だけで処理してファイルやlocalStorageに残さないようにする

パッケージ側の実装にもよるが、 access_token が失効したら再取得する方法であればファイル等に保存する必要が無い。

ということで、先に設定したポリシーについてはすべてクリアできている。

最後に

てっとり早く作れるAtomのパッケージだが、あまり深く考えずに作るとセキュリティ上問題が発生する恐れもある(未だに以前の方法だと本当にセキュリティ上問題があるのかがよく分かってないが)。
かと言って「セキュリティって考えるの面倒だよね。面倒だからこの機能実装するのやーめよっと」とならずに、これがAtomのパッケージにかぎらずクライアント側のOSSでOAuth2を利用する人達の参考になれば幸いです。

今回外人さんからメールをもらって改めて考えてみたのだが、結果的なことよりもOAuth2自体の勉強もできたしWebviewの存在を知ったことが良かったと思う。

ということで、 Atom用Gmailチェッカー の新バージョンをリリースしました。
今回の修正で安心してお使いいただけますので、よろしくお願いします。

ってか、なげーよww

nobuhito
無いものは作る、欲しい物も作る
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away