2010年08月24日

DevQuiz2010のOAuthをtelnetで倒す

意外にも私以外にtelnetで倒したという話を聞かないので、何となく書いてみる。
Try and Errorでがちゃがちゃとやっていたので、内容的には少し順不同です。
(出来るだけ分かりやすい順序で書いてみました)

問題文は…転載していいのか分からないので省略。
このエントリ見てる方は皆さんご存じですよね、きっと。

まずはOAuthって何ぞ…と思って調べてたどり着いたのがこちらのページ。
(そんな奴がGDDに行こうとするなよ、と言うツッコミは置いといてw)

OAuthプロトコルの中身をざっくり解説してみるよ - ゆろよろ日記
http://d.hatena.ne.jp/yuroyoro/20100506/1273137673

Yahoo!デベロッパーネットワーク - OAuth - OAuthを用いたAPIリクエスト
http://developer.yahoo.co.jp/other/oauth/api.html

なるほど、どうやらAuthorizationヘッダとやらを送りつければいいようです。

各種パラメータについて、とりあえず問題文に以下の4つは指定がありました。

・認証のrealm:devquiz
・Consumer key: 7fded0d291561d16a4b8a651
・Consumer secret: a828fa17fdb32c3cf0956cb3
・署名方式:HMAC-SHA1

これでrealmoauth_consumer_keyoauth_signature_methodの3つは設定値が分かりました。

で、あとは分からない物を潰していく。

まずはoauth_nonce。これは何でもいいみたいだったので、上記のYahoo!で使っているサンプル値そのままにしてしまいました。

続いてoauth_timestamp。これは所謂UNIX時間を設定するようなのですが、求め方がよく分かりません。んで、ぐぐるさんに調べてもらったら以下のページを見つけました。

エポック秒(UNIX時間)変換マシーン
http://egg-in-oven.hp.infoseek.co.jp/epochsec.html

ボタン一発で計算終了。ありがたい事です。
ただ、ここで指定した時間と現在時刻が600秒以上ずれると認証が通らなくなってしまうようなので、作業はサクサクと進める必要がありました。

最後に残ったのがoauth_signature。これがものすごく複雑なんですが、以下のページに詳細な解説があったのと、間違えた時にDevQuizのサーバが「署名するのはこの文字列のはずだぜ」とご丁寧にも教えてくれたので、署名の方法自体は何とかなりました。

Yahoo!デベロッパーネットワーク - OAuth - リクエストの署名
http://developer.yahoo.co.jp/other/oauth/signinrequest.html

ただ「HMAC-SHA1アルゴリズムを利用して16進のダイジェスト値の生成→Base64エンコード→URLエンコード」という仕組みは分かっても、実際にどうやったら値が求められるのかさっぱり分かりません。前述のエポック秒(UNIX時間)変換マシーンのような一発変換のページを探してみたんですが、残念ながら見つけられず。

ここでうぬぬーと頭を抱えていたのですが、以下のページでPythonで実装されたOAuthのサンプルコードを発見しました。

Twitter API を OAuth で認証するスクリプトを 0 から書いてみた - trial and error
http://techno-st.net/2009/11/26/twitter-api-oauth-0.html

うん、どうやらPythonを使えば何とか出来そう。
とは言えPythonで全てを実装出来るほどのスキルはないので、署名の部分だけ対話型で打ち込んで何とかしてしまいました。
import time, random
import urllib, urllib2
import hmac, hashlib
hmac.new("a828fa17fdb32c3cf0956cb3&", "POST&http%3A%2F%2Fgdd-2010-quiz-japan.appspot.com%2Foauth%2F7fded0d291561d16a4b8a651&hello%3Dworld%26oauth_consumer_key%3D7fded0d291561d16a4b8a651%26oauth_nonce%3D24829.2331%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1281716837", hashlib.sha1).digest().encode("base64").strip()

これで最難関の署名が完成。

あとはPOSTメソッドでhello=worldというパラメータを送信するという指定なので、最終的には以下のようなデータを送る事になりました。

telnet gdd-2010-quiz-japan.appspot.com 80

POST /oauth/7fded0d291561d16a4b8a651 HTTP/1.0
Host: gdd-2010-quiz-japan.appspot.com
Authorization: OAuth realm="devquiz", oauth_consumer_key="7fded0d291561d16a4b8a651", oauth_signature_method="HMAC-SHA1", oauth_nonce="24829.2331",oauth_timestamp="1281716837",oauth_signature="Guti8N50so36JFGLtKIpC9XmdnI="
Content-length: 11

hello=world

これで「200 OK」。

問題の趣旨とはだいぶ解答方法が異なるような気がするのですが、うまく行ったので良しとします。直前にソースコード提出が必須になるという話があった時は正直ビビりまくりでしたw

個人的には、この問題のおかげでOAuthの仕組みをちょっとだけ理解出来たのが一番の収穫でした。今はOAuthを使ったGmail送信用のAndroidアプリを作成中。(いや、まだ一行もコード書けてないんですけど) それが完成したら頑張ってtwitterクライアントを書きたいなと妄想中。twiccaみたいなすごい奴じゃなくて、投稿/Reply専用の俺得クライアントになる予定ですが。

たまに自分を奮い立たせてくれるような宿題とかイベントがあるのはいいですね。
GDDに行けるかどうかは置いといて、おいらもっともっと頑張らないとな。
posted by 月水和尚 (とも) at 15:06 | Comment(0) | TrackBack(0) | その他もろもろ