pblog

pplog.net を作っている @ppworks こと越川直人(Koshikawa Naoto)のブログ。文体は日によって違う。esa LLCで働いてます(\\( ⁰⊖⁰)/)

mixiアプリからの署名付きリクエストの受け側を作る

前回、署名付きリクエストで外部サーバへデータを保存する方法を見てきましたが、そんでは受け側はどうやって作ろうかというお話です。

外部サーバのAPIはPHPとMySQLを用いて作成することを前提とします。

** 署名の確認 OAuthの署名を用いて、リクエストがコンテナから発行されたものであることを確認します。

このときリクエストに含まれる -oauth* -opensocial -xoauth_ といったパラメーターは、コンテナがリクエストの際に付与するパラメーターです。

これらはOAuthの認証や、アプリケーションの情報を確認するために使用します。よって同名のパラメーターは指定出来ない事に注意します。

たとえば、パラメータとして以下のようなパラメータを渡すと

|| opensocial_owner_id=123456789 ||< とか || opensocial_hogehoge_id=123456789 ||< OpenSocialコンテナがリクエストを発行する前に、このように怒られてしまいます。 || HTTP ERROR 400 Problem accessing /gadgets/makeRequest. Reason: invalid parameter name opensocial_owner_id ||< それっぽいパラメータ名はさけた方が良さそうですね。

OAuthの署名の確認についての具体的なサンプルは、署名付きリクエストで外部サーバへデータを保存するにありますので、そちらをご覧下さい。

さて、公開鍵を格納するのには、以下のようなテーブルを用意しておくと便利です。

|| CREATE TABLE IF NOT EXISTS mt_container ( container_id tinyint(3) unsigned NOT NULL default '0', container_name varchar(255) collate utf8_unicode_ci NOT NULL default '', container_oauth_consumer_key varchar(255) NOT NULL default '', container_certificate text NOT NULL COMMENT 'コンテナの署名', PRIMARY KEY (container_oauth_consumer_key), UNIQUE KEY container_id (container_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ||<

*** mt_containerテーブルの説明

container_id
コンテナのユニークなキーを割り当てます。ユーザはコンテナごとに存在するので、このコンテナキーと各コンテナで定義されたユーザーIDを組み合わせてユーザを識別します。
container_name
mixi、goo homeなどを書いておくようです。
container_oauth_consumer_key
コンテナを識別するために使います。署名付きリクエストで付与されるoauth_consumer_keyパラメータと対応します。
container_certificate
各コンテナが署名に使う鍵の公開鍵です。mixiとgoo homeでは以下に公開されています。 -mixi Developer Center (ミクシィ デベロッパーセンター) » 外部サーバの呼び出し -外部サーバーと通信する - goo Developer's Kitchen

** アプリケーションの確認 ここまでの確認は、リクエストが特定のコンテナからのものである事を確認するものでした。しかし、apiを同じコンテナの別のアプリケーションから利用されたくない場合には、更なる対策が必要となります。

一つの方法を紹介します。 -まず、api側に使用を許可するアプリケーションのidや、gadget urlを登録しておきます。 -次に、リクエストに含まれるそれらの情報を照らし合わせて、apiの使用の可否を判断します。 -以下に示すようなテーブルを用意して、認可をおこないます。テーブルを拡張することにより、アプリケーションごとに許可するapiを振り分ける事といった事も考えられます。

テーブルはこのようなものが考えられるでしょう。

|| CREATE TABLE IF NOT EXISTS mt_app ( app_id varchar(255) NOT NULL default '', container_id tinyint(3) unsigned NOT NULL default '0', PRIMARY KEY (app_id,container_id) ) ENGINE=InnoDB DEFAULT; ||< *** mt_appテーブルの説明

app_id
コンテナからの署名付きリクエストで付与されるopensocial_app_idパラメータと対応します。
container_id
先に説明したmt_containerテーブルで指定したコンテナのIDです。

署名付きリクエストのopensocial_app_idパラメータがこのテーブルに登録されているかを確認して接続許可の可否を判断しましょう。

** データのキャッシュ 外部のapiを利用するシーンを考えると、データを生成する事や、データをへ保存、取得するようなシーンがありそうです。それらのデータをデータベースへ保存、参照する際にはキャッシュの利用を考えておくとのちのちのためになりそうです。

共用レンタルサーバを利用している際には、memcachedなどを使うのも難しいでしょうから、データベースの内容をファイルにキャッシュする、そもそも外部リクエストを発行する際にキャッシュを有効にする、などが現実的かも知れません。

外部リクエストの発行時にキャッシュを有効にするには以下のようにします。

|javascript| var callback = function(res) { console.info(res.text); // firebugs用 }; var url = "http://{your_host}/someapi"; var params = {}; // ここでキャッシュの時間を指定(秒数) params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = 120; params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET; params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED; gadgets.io.makeRequest(url, callback, params); ||<

** ファイル名のhash化 apiが提供、または生成するようなファイル等を簡単に推測される名前は避けたいという場合の解決策を考えます。

ユーザID:2000の、画像ID:1234のパスが以下にあることすぐに分かる例。

|| http://{your_host}/img/2000/1234.jpg ||<

これを推測しずらくするためには、hash関数を使えば行けそうです。今回はHMAC-SHA256を利用してみました。

phpでは以下のようにhash値を求める事が出来ます。hashのシークレットキーをsecrethogeとします。

|php| hash_hmac("sha256", "変換したい文字列", "secrethoge"); ||<

先ほどの例で、画像ID:1234のhashを求めて、パスを生成したとすると以下のようになります。

|| http://{your_host}/img/2000/99a0825aa452fd31e7b838a0defd10f2168a4a07125cf3ef5282f48ba4a9b114.jpg ||<

同じ文字列から同一の一意なキーを求めるには、同じシークレットキーを必要とするため、推測しずらくなります。

** まとめ

外部api自体は比較的簡単に実装することが可能ですが、先を見据えたつもりになって設計することが大事だと思います。次は、実装方法を考えて行きたいと思います。