読者です 読者をやめる 読者になる 読者になる

pblog

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

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

OpenSocial mixiアプリ

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

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

署名の確認

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

このときリクエストに含まれる

といったパラメーターは、コンテナがリクエストの際に付与するパラメーターです。

これらは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では以下に公開されています。


アプリケーションの確認

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

一つの方法を紹介します。

  • まず、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などを使うのも難しいでしょうから、データベースの内容をファイルにキャッシュする、そもそも外部リクエストを発行する際にキャッシュを有効にする、などが現実的かも知れません。

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

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とします。

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

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

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

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

まとめ

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