こんばんはこんばんは!!、ritouです。
木曜に公開されたこの記事を見て実際に試してみた/使ってみたってエントリ、たぶん誰も書いてくれないので自分で書きます。
OAuthのセキュリティ強化を目的とする拡張仕様を導入しました - mixi engineer blog
とりあえず上記の記事を読んで、最後の方のシーケンス図を覚えといてください。
では動作確認を始めましょう。
手順0 : サービスを登録してclient_id, client_secretの取得
もしかして、mixi Platformの挙動だけ確かめたい人にとってはここがハードル高いのか。まぁしょうがないです。
開発者登録している人はこちらからサービスを登録できますね。 https://sap.mixi.jp/connect_consumer.pl
例として、とりあえずこんな感じで登録してみます。
- client_id : 4f7511d31ca24d9d62c7
- client_secret : (秘密)
- redirect_uri : http://localhost/callback
手順1:server_stateの取得
tokenエンドポイントにserver_stateを要求します。
(なんでtokenエンドポイントでserver_stateを返すのかっていうと答えに詰まりますが、余計な専用エンドポイント増やすのもあれかなと思っただけです。)
$ curl -i -d "grant_type=server_state&client_id=4f7511d31ca24d9d62c7" https://secure.mixi-platform.com/2/token HTTP/1.1 200 OK Date: Sat, 1x Nov 2013 xx:xx:xx GMT Server: Apache Cache-Control: no-store X-MIXI-GRAPH-API-SPEC: 131072 Vary: Accept-Encoding Content-Type: application/json X-Content-Type-Options: nosniff Connection: close Transfer-Encoding: chunked {"expires_in":1800,"server_state":"4RniCttOveIpCuZyYFF8Z-NlTqOY5cFvBnDLCsqwCkc"}
有効期限は30分です。
手順2:ユーザーのアクセス許可
こんなURLにアクセスします。
server_stateパラメータを含みます。
https://mixi.jp/connect_authorize.pl? client_id=4f7511d31ca24d9d62c7& response_type=code&scope=r_profile& server_state=4RniCttOveIpCuZyYFF8Z-NlTqOY5cFvBnDLCsqwCkc
こんな感じの画面が出ますね。
アクセス許可すると、redirect_uriに戻ってきます。
http://localhost/callback?code=18866a25f0ec546f1702887e394c3905c85e97e4
これが認可応答です。
手順3:アクセストークン等を取得
codeパラメータを含む認可応答を受けたら、セッションに紐づけているserver_stateを一緒に送ってトークンを要求します。
$ curl -i -d "grant_type=authorization_code&client_id=4f7511d31ca24d9d62c7&client_secret=(秘密)&redirect_uri=http://localhost/callback&code=18866a25f0ec546f1702887e394c3905c85e97e4&server_state=4RniCttOveIpCuZyYFF8Z-NlTqOY5cFvBnDLCsqwCkc" https://secure.mixi-platform.com/2/token HTTP/1.1 200 OK Date: Sat, 1x Nov 2013 xx:xx:xx GMT Server: Apache Cache-Control: no-store X-MIXI-GRAPH-API-SPEC: 131072 Vary: Accept-Encoding Content-Type: application/json X-Content-Type-Options: nosniff Connection: close Transfer-Encoding: chunked {"refresh_token":"(秘密)","expires_in":900,"access_token":"(これ一番秘密)","token_type":"Bearer","scope":"r_profile"}
不正な組み合わせだとエラーが返されます。
- codeにserver_stateが紐づいているが、指定されたserver_stateの値と異なる
- codeにserver_stateが紐づいていないのにserver_stateの値が指定された
- codeにserver_stateが紐づいているのにserver_stateの値が指定されていない
まぁ、これだけだったりします。
ここでPerlのライブラリの紹介です。
OAuth::Lite2だと簡単にできるよ
OAuth 2.0のServer/Clientの両方のための実装が詰まっているOAuth::Lite2っていうCPANモジュールがあります。
https://metacpan.org/release/OAuth-Lite2
上記の認可フローを利用するClientはOAuth::Lite2::Client::WebServerを使うと捗ります。
https://metacpan.org/pod/OAuth::Lite2::Client::WebServer
というのは、下記の処理が実装されているからです。
このOAuth::Lite2::Client::WebServer、最近get_server_stateっていうメソッドが追加されていて、それが上記のserver_state取得要求を実装しています。
なので、雰囲気としてはこんな感じで書くと動くはずです。
my $client = OAuth::Lite2::Client::WebServer->new( id => q{my_client_id}, secret => q{my_client_secret}, authorize_uri => q{http://example.org/authorize}, access_token_uri => q{http://example.org/token}, ); # 認可要求を送るとき sub start_authorize { my $your_app = shift; # server_stateを取得 my $server_state = $client->get_server_state() or return $your_app->error( $client->errstr ); # 取得したserver_stateをセッションに紐づけておく $your_app->session->set_server_state($server_state); # extraに含めます my $redirect_url = $client->uri_to_redirect( redirect_uri => q{http://yourapp/callback}, scope => q{photo}, extra => { server_state => $server_state->server_state, }, ); $your_app->res->redirect( $redirect_url ); } # 認可応答を受けてアクセストークンを要求するとき sub callback { my $your_app = shift; my $code = $your_app->request->param("code"); # セッションからserver_stateを取り出す my $server_state = $your_app->session->get_server_state(); # access_tokenを取得するメソッドの引数に渡す my $access_token = $client->get_access_token( code => $code, redirect_uri => q{http://yourapp/callback}, server_state => $server_state->server_state, ) or return $your_app->error( $client->errstr ); $your_app->store->save( access_token => $access_token->access_token ); $your_app->store->save( expires_at => time() + $access_token->expires_in ); $your_app->store->save( refresh_token => $access_token->refresh_token ); }
(これは$your_appとか書いてるのでコピペだけじゃ動きませんよ)
また今度時間のある時にデモClientとか作って紹介します。
まとめ
- 使ってみた
- 対応してるライブラリを紹介した
そういえば
今気づいたけど「カスタム URL スキーム上書き Authorization Code 横取り問題」の解決にもなってる?
2013-11-14
あるモバイルアプリからAuthorization Code Grantを使う認可フローを使う場合、
- Client Credentials(client_id, client_secret)をリバースエンジニアリングとか通信覗くとかして取得する
- redirect_uriを揃える
とかされて、正規のアプリ->アクセス許可->悪意のあるアプリとユーザーが遷移していくとAuthorization Codeからaccess_tokenとられてやられたわーなんていう可能性があります。
今回のserver_stateを使っていれば、Authorization Codeだけ取得してもaccess_tokenが取得できません。イイネ!
モバイルアプリだからclient_secret発行しない!っていってImplicit Grant提供しているところとかありますけれども、アクセストークン置換攻撃を気にするのであれば
- server_stateの利用を必須にする
- client_secretが空のAuthorization Code Grantを使う
とかにすると少しはマシになるのではとも思います。拡張仕様を考えるときにちょっとその辺を意識して、client_idだけでserver_state取得ができるようにしています。
ではまた!