2014年01月26日

Twitter API に HTTPS でアクセスする(ただし、C++Builder + Indy)

さて、2014年1月15日から、Twitter API のアクセスが、HTTPS 限定となりました。
「節気さんシリーズ」は、ながらく、HTTP であくせすしてていたので、この時、全面的にBotが停止するという事態に陥ったわけであります。

ちかいうちに、そうなりそうだなという話は聞いていたのですが、HTTPS への対応が分からなかったので、「見て見ぬふり」をしていたら、いきなり、痛い目に遭ったという、そういう構図です。

実際に止まってしまったので、あわてて対応方法法を調べます。
基本的なこところは、
http://www.gesource.jp/programming/bcb/45.html
で、判明。
C++Builder についてくる、Indy ライブラリを使えば、SSL で、POST は、容易に実現できそう。
特に、従来、TCPClient でオープンして、レスポンスをパーシングして、

ちょっと安心して、これまで作っていた、OAuth データを、「POSTパラメータ」にぶちこんで、POST するも、失敗。
なぜだろうかと、数日悩んだ結果、答は、
http://d.hatena.ne.jp/mole-studio/20130816/1376676005
にありました。

OAuth ヘッダは、Request.CustomHeaders にセットして、
パラメータのみ、(POST の引数としてわたす)パラメータにセットすれば、OKでした。

この段階で、ツイートには成功しました。
ただ、もうひとつの機能である、「アイコンの差し替え」は対応できていません。

Botの動作は、「OAuth ヘッダを送って、認証した後、アイコンファイルを、マルチ-パートの扱いで直接送信」でしたので、Indey ライブラリを使った、ファイルのアップロードを試すも失敗。
ここで、もう一度、Twitte API の仕様を確認すると、「アイコンファイルは、Base64でエンコードした後、image= につづけて、パラメータとして送信すること」という内容を見つけました。

Base64でエンコードするの処理が追加で必要になるものの、POSTの処理自体は、ツイートと同じなので、楽勝――のはずが、これまた失敗。

悩むこと数日、答がひらめきました。
OAuth ヘッダ(のための、Base String)を作る際に、関連するヘッダは、「アルファベット順に並べる」訳ですが、ツイートの際のパラメータは、status = で、「たまたま」一番最後でした。
この処理を使ったので、アイコン送信の際のパラメータである、image = も「最後」においたわけです。
そもそも、「ヘッダはアルファベット順」でしたが、「ヘッダの後にパラメータ」ということで。
(ちなみに、Twitter サイトの説明もこの順番だった)

でも、image は、「アルファベット順」だと、最初だったのですね。
順番を入れ替えて、Base String をつくり、OAuth ヘッダを作ることで、無事に処理が成功しました。

1週間半。長い戦いでした。

posted by 麻野なぎ at 16:51| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年08月17日

OAuth は、どう安全なのか?(物語風に)

さてさて、このお話は、どこにあるのかわからない、「ツイット国」のお話です。
実は、この国の人々は、かけ算は得意ですが割り算は苦手……というより、存在すら知りません。
ちょっと変な仮定ですが、物語風 OAuth の説明に必要な仮定ですので。

【はじまりはじまり】

この国に、ツイット太郎という若者がいました。
太郎は、近所のツイット亭というレストランが大のお気に入り。三度の食事をもれなくここで食べていました。
そうなると、面倒なのがお金の支払いです。
そうは言っても、太郎も悪人ではなく、単にお金を持ち歩くのが面倒なだけでした。
そこで、太郎は考えます。
そうだ、ツイット亭の人に、ツイット銀行まで行って、自分の口座から代金を引き落としてもらえば良いんだ。

そうして、太郎は、ツイット亭の人に定期的に銀行まで行くようにお願いし、ツイット亭の人も快く引き受けてくれました。

【Basic 認証の時代】

この時代では、太郎は、お店の人に2つのことを教えます。
1)口座番号(ここでは、12345678 とします)
2)暗証番号(ここでは、7777 とします)
以上です。

お店の人が銀行に行ってお金を引き出すようです。
亭:もしもし、口座番号 12345678 からお金を引き出したいのですが。
銀:では、暗証番号をどうぞ
亭:7777 です。
銀:暗証番号はあっています。では、必要なお金をどうぞ……

こういう会話が交わされます。
……危ないですね?

危ないと言っても、お店の人が太郎の食べてないもののお金まで引き落としてしまうかもしれない……というお話は、今回は、扱いません。
そもそも、そんなおかしなことをするかもしれないお店に、引き落としの代行は依頼しないだろうというのが、基本方針です。

もうひとつの危険性。
もしも、この会話を悪人が聞いたらどうなるでしょう。
早速、ここで聞き耳を立てていた悪人がいたようです。

悪:もしもし、口座番号 12345678 からお金を引き出したいのですが。
銀:では、暗証番号をどうぞ
悪:7777 です。
銀:暗証番号はあっています。では、必要なお金をどうぞ……
悪:しめしめ……。

ただ単に聞いていたことを繰り返すだけですから、簡単なことです。さらに悪いことには、銀行には、「だれがお金を下ろしたか」という記録は一切残りません。
銀行としては、正しい口座番号と暗証番号を知っていたのだから、正しいお客様だと判断するしかないのです。

【OAuth の時代】

さて、Basic 認証はあまりに危険だ……特に、お金を引き出すときの会話を盗み聞きされたどうしようもない。
そこで、時代は、OAuth の時代になりました。
ここでは、ツイット太郎は、ツイット亭と、さらに、ツイット銀行を交えて、4つの情報を決定します。
1)お店の「店舗番号」(ここでは、00110022 とします)
2)お店の「暗証番号」(ここでは、8888 とします)
※ここまでの情報は、「アプリケーションの登録」をしたときに決まります。ユーザーには直接関係ありません。(Twitter が割り当てます)
3)太郎さんの「ツイット亭専用」口座番号(ここでは、11223344 とします)
4)太郎さんの「ツイット亭専用」暗証番号(ここでは、1111とします)
※最後の2つは、「OAuth を承認」したときに決定されます。(Twitter が割り当てます)

さて、今回もお店の人が銀行に向かいお金を下ろします。

亭:店舗番号 00110022 のものですが、11223344 の口座からお金を下ろしたいのですが。
銀:では、最初に、「一回だけ番号」を適当に言ってください。
亭:じゃ、6543 にします。
銀:わかりました。では、「しぐねちゃ」をおしえてください。
亭:えっと〜
  ここで、ツイット亭の人は、「しぐねちゃ」を計算し始めます。
  計算方法は、
  1) 店舗の暗証番号(8888)と、お客様の暗証番号(1111)をくっつけて、88881111 という数字にする。
  2)それに、「一回だけ番号」の、6543 をかける
  3) 結果は、88881111×6543=1728524313
亭:しぐねちゃは、1728524313 です。
銀:正解です。では、必要なお金をどうぞ……。

さてさて、今回も悪人が聞き耳を立ています。

悪:店舗番号 00110022 のものですが、11223344 の口座からお金を下ろしたいのですが。
銀:では、最初に、「一回だけ番号」を適当に言ってください。
悪:じゃ、6543 にします。
銀:その番号はさっき使ったのでだめです。ちゃんと、「一回だけ番号」を言ってください。
悪:困ったなぁ〜 適当に、9876 です。
銀:わかりました。では、しぐねちゃをおしえてください。
悪:しぐねちゃあれ? 暗証番号がわからないから、計算できないよ。

ということで、聞き耳をたてていても、失敗に終わるという仕掛けです。

【まとめ】

・OAuth では、暗証番号に相当するものを相手に伝えるかわりに、「それを知らなければできない計算の答」を伝える
 だから、聞き耳をたてていても、暗証番号に相当するものは奪えない。
・OAuth では、「一回だけ番号」に相当するものがあって、毎回送信する情報が変化する。
 だから、聞き耳をたてて、送信されたものを再利用することができない
 (実際には、送信する時点の時刻情報も併せて使います)
・OAuth では、ユーザー情報は、「そのお店専用」になっている。
 だから、直接誰がお金を引き出したか(誰が、ツイートしたか)がはっきり残ってしまう

こんなことをして、承認していない相手に情報が漏れ、不正に使われることを防止しているわけです。

【注意】

このお話の前提となっているのは、「ツイット国の人々は割り算ができない」ということです。
現実の世界では、88881111×6543=1728524313 という計算の答え(しぐねちゃ)と、一回だけ番号(6543)がわかっているのですから、割り算をすることで、88881111 というお店と太郎さんの暗証番号がわかってしまいます。
実際、ここの計算は、単なるかけ算ではなくもう少しややこしい計算が使われています。
ただし、このお話のように、計算するのは(コンピュータなら)簡単だけれど、答えからもとの数字を割り出すのは非常に困難という、そういう計算が使われています。

この例を含めて、技術的には、正しくない内容が含まれています。
また、「4つの情報を決定します」とだけ書いた、決め方の部分もかなり面倒なやりとりがあります。
まあ、そういう点を全部無視していますが、なぜ、OAuth のほうが Basic 認証より安全なんだ? というイメージとしては、大きくは間違っていないと考えています。
posted by 麻野なぎ at 22:12| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年08月05日

OAuth への道 ―― OAuth の危険性にどう対応するか

この記事は、「OAuth の危険性」
http://asano-nagi.sblo.jp/article/39973329.html
の続編である。

そこで、OAuth の危険性にどう対応するかということでまとめようと思うが、最終的に、当たり前の結果しか出てこないであろう事は、いたしかたない。

【(もしかしたら)Twitter サイドの問題】

さて、OAuth の危険性という側面で、Twitter サイドの設定に触れておこう。
実は、OAuth という仕組み全体の中では、OAuth の認証をする際に、「どの程度操作を許可するか」という設定や、「いつまで操作を許可するか」といった設定ができることになっている。
しかしながら、Twitter の設定では、有効期限は「無期限(ただし、ユーザーが「拒否」に設定するまで」。操作権限は、「読み込みのみ or 読み書き」という設定しかできない。
言い換えると、「ポストが可能なサービスなら、あらゆる操作が可能」ということである。

Twitter というサービスの持つ性格、「かなりお気楽にポスト」という事から考えると、せめて「ポストのみ」というレベルの制限はあった方が良いのではないか? というのが、よく言われることである。
(実際にはそうなってない)

【われわれは、かつてどうしてきただろうか?】

この問題を考える際、「ユニクロ行列キャンペーン」(たとえば、http://mainichi.jp/select/biz/it/japan_internet_com/archive/2010/05/31/wmnews_20100531_8.html ) がひとつの参考となる。
ユニクロの仮想店舗にこれまた、仮装行列を作るというもので、行列をするために、Twitter の ID と パスワードが要求されたというものである。
この場合、いくつかの対応が可能だっただろう。

1)なんかおもしろそうだから、何も気にせず、ID と パスワードを入力する
2)ID と パスワードを入力するのは一般的には危険だけど、ユニクロだから信用して入力する
3)おもしろそうだけど、ID とパスワードの入力は断固拒否する(で、参加しない)

このうち、1)の対応はまずいと思う。しかし、2)は、「自己責任」として、ありだと思う。
他にも、「危険性」の記事で取り上げた、「メールソフトの作者は、(技術的には)すべてのメールにアクセスできる」という話にしても、一般的な危険性を知った上で、それでも、信用できるという判断で、サービスを使用してきたわけである。

なお、「自己責任」というが、たとえば、「煙草には肺がんリスクがある」と明示された上で、「それでも吸う」のは「自己責任」である。肺がんリスクそのものが伏せられていた場合、それで吸うのは自己責任とはいわない。

だから、たとえば、「タイムラインごちゃ混ぜポストBot」が、「あなたにOAuthの許可を求めています」というのが、実は、「タイムラインをごちゃ混ぜにした内容をポストする」ということの許可を求めているのではなく、「そのBotが、目的以外のことはしないだろうと信用して、あなたのアカウント操作を認めるか」ということであるというのを、まず、知って、その上で、あるサービスが有用だったり、おもしろそうだったりで、「承認する」という、自己責任のための情報は提供しなければならないと思う。

【では、どう対応するのか?】

まず、OAuht を承認したアプリケーションに対して、「拒否をする」方法を理解すべきである。
Web 上でログインし、設定 → 連携アプリ で該当するアプリケーションの「許可を取り消す」ことでこれは可能である。
問題が発生した場合、まずは、許可の取り消しが可能であるという状態にしておく必要はある。
※ちなみに、パスワードを変更しても、効果がないので注意。

その上で、そのサービスが信用できるか判断する、判断できなければ使わないと、そういう当たり前の対応しかないのだと思う。
ただ、考えてみれば、Twitterに限らず、各種存在しているサービスを、個人情報を入力した上で利用しているとすれば、それは、「信用できる」という判断があったのだと思う。
Twitter で、OAuth に対して承認を行う場合も、考え方は同じである。
ただ、「単に、(たとえば)タイムラインが読まれてポストされるだけ」ではないよと、そういう意識を持つことは必要なのである。

【本当の脅威は静かにやってくる】

以上述べたとおり、OAuth に対して、何も考えずに承認を与えるというのは危険である。
ただ、今回、通称、「なる四」など、OAuth 周辺の騒動から、OAuth はもしかしたら危険なのかもしれないという話がすこしばかり、一般的になったわけである。

しかし、これは、「なる四」だけが危ないわけでも、OAuth だけが危ないわけでもない。
むしろ、本当に悪意を持ったなら、こんなに大騒ぎをして、「悪巧み」をぶちこわすはずはないと、そういう点は意識すべきである。

そして、数ある(Twitter ではない)Web サービスも、「技術的には」やはり危ないのである。
それを含めて、「どう対応するか」というのを、一度考えるべき時ではあると思う。


【おまけ・ソーシャルクラッキングを考える】

この記事を書いているうちに、

「なるほど4時じゃねーの解除ツール」 ← 嘘ですhttp://spreadsheets.google.com/viewform?formkey=dDJQbVZlZVE0T3BXelh1S004ZWZqZkE6MQ

なるものを見つけることができた。
(この記事を書いている 2010年8月5日の段階では、表示させるだけなら実害はない)

これを見ると、「なるほど4時じゃねーの解除ツール」で、解除をするために、Twitter の ID とパスワードを入力せよというフォームになっている。
この作者は、少なくとも、「凶悪」な方ではなく、「それやっちゃいかんだろう」と、ご自身で指摘してくれる構造になっているわけだが(あ、それでも、最後まで突っ走ると、入力された ID と パスワードを、保存してしまうらしい・作者談)いきなり、このようなフォームを見つけて、「まずい」と、そもそも入力せずに無視することができるだろうか?

また、ここに書いたこと(作者が、「凶悪」ではないとか)も、実際に試してみて(オイ)そこに書かれた文字を信用して書いているだけで、作者が凶悪でないふりをして、実際には、入力された情報をいきなり保存している可能性もある。
また、フォームの作者らしい方のIDも書かれているが、本当に、その方が作者だという保証もない。

と、ようするに、非常に危険(かもしれない)なサイトなのであるが、これは、一瞬で「危険」だと見破らなければならないと、そういうわけである。

こういう、人間の特性を悪用したクラッキングは、「ソーシャルクラッキング」と呼ばれている。
たとえば、「システム管理課の××です。アカウント管理サーバーがクラッシュしたので、確認のため、パスワードを教えてください」とか、「××銀行の○○です。あなたの口座に入金があったのですが、機械のトラブルで入金ができていません。確認のために暗証番号を教えてください」とか。

冒頭の例に似たものには、「パスワード評価サイト」を見かけたことがある。
「パスワードの安全性を判定します。パスワードを入力してください」
その結果は……こんな得体の知れないサイトでパスワードを入力すると悪用されますよ。

いずれにしても、情報の入力には気をつけましょう……と、そういうことである。
posted by 麻野なぎ at 17:27| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年08月04日

OAuth への道 ―― OAuth の危険性

この記事を書こうとしているとき、Twitter 界隈では、"OAuth の危険性・悪用" に関連した話題がささやかれている。
そこで、本当に、OAuth は危険なのかということについて書いてみることにする。

今、OAuth の危険性として話題になっているのは、いくつかの側面がある。
何かが、危険であるとか、安全であるとかを話題にするとき、「どういう面が危険とされているのか?」という切り分けは重要である。

【機能的な側面】

OAuth は、もととも、「当人が通常の操作を行うところを、代行する」ために準備された機能である。Titter の場合、通常の動作とは、Web 上でログインした後、タイムラインを読んだり、ポストを行ったり、それこそ、ダイレクトメールを読んだり書いたりというそういう操作を意味する。

これ以外の操作は、すべて、「代行されたもの」だと考えて良い。
たとえば、Twitter のクライアントを使用するのも、「代行されたもの」である。
とすれば、OAuth を承認することで、「代行可能な操作」は、クライアントで実行可能な全操作であり、言い換えれば、ユーザーが実行可能なほとんどすべての操作ということになる。
当然、ダイレクトメールの送受信、お気に入りの設定なども可能である。

しかも、これらの機能を「自動的に」実行可能な点に注意しなければならない。
ポストすべきメッセージはキーボードから入力し、読むべきメッセージは画面に表示されるというケースが多いと思われる。
けれど、何も、メッセージはユーザー自身がキーボードから入力しなくても、クライアントなり、第三者サービスが、勝手に生成して送信することもできる。
読み込まれたタイムラインは、表示しないまま、どこかにデータとして送信することもできる。

さらにいえば、「公開されていない」メッセージも、自分がフォローしている場合には「読める」というのも注意しておきたい。
多くのクライアントでは、公開されていないメッセージは、ReTweet できないようになっているが、これも、「公開されていない」という目印を認識して、対応しているに過ぎない。
「公開されていない」メッセージであっても、読むことができる以上、(技術的には)送信できるわけである。

【危険性が周知されていないという危険性】

さて、上述したのは、多くの場合において、「大げさ」ではある。
確かに技術的に可能ではある(しかも、OAuth 認証に対応しているアプリケーションとしては、技術的には難しいものではない)
ただ、それは、「メールソフトの作者は、(技術的には)ユーザーのメールを自由に読むことができる」というのと、同じ指摘であるに過ぎない。

特に、いわゆる Web メールで、「他のメールサーバーのメールも読めるような設定が可能です」という場合、(技術的には)ユーザーのメールを自由に読むことができて、なおかつ、ユーザーはそれに気づかないというのも、そんなに難しいものではない。

ただし、メールソフトに関しては、この説明で多くの人が「確かに、技術的にはそうなのだろう」と納得はできると思う。
メールソフトとして、メールの読み書きをしているのだから、そのメールをたとえば、谷転送したりすることは技術的には可能だというのは、十分想像できることである。

ところが、Twitter の場合、もう少し別の側面が存在する。

たとえば、「あなたのアカウントのタイムラインの発言を、適当に混ぜ合わせて、一日に一回あなたのアカウントからポストします」という、ちょっとお手軽なアプリケーションがあったとしよう。
おそらく、Twitterで、OAuth の認証が求められる。

「あなたのタイムライン、ぐちゃぐちゃまとめますサービス」が、認証を求めています。
・許可する ・拒否する。

ここで、このサービス(多分Bot)が、タイムラインを読んで、適当な言葉を混ぜ合わせて、それをポストするだけの機能だったら、そんなに難しいことを考えずに、許可しても良いだろうと思う。
しかし、このBotもまた、「技術的には」あらゆる操作が可能なのだ。

だから、許可すべきではない……とはいわない。
いわないが、少なくとも、このサービスがそこそこ使われているか? 少なくとも現時点では、問題を起こしてないか? そして、何かあったときに、改めて拒否するにはどうしたらいいか、そういうところまでを、確認した上で、許可するのが良いのではないだろうか?

OAuth の認証は、見かけ上、「かくかくしかじかのサービスを許可するか」という質問に見える。
しかしながら、実際は、「こういうことを言っているサービスがあるが、このサービスを信用して、あなたのアクセス権を譲渡しても良いか?」と尋ねているのである。

時間である。
続きは、また、近いうちに……。(オイ)
posted by 麻野なぎ at 12:44| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年08月02日

Turbo C++ (というか、BCB というか、C++ Builder)で、DLL の「共有セグメント」を使う安直な方法

【結論】

・共有セグメントに置きたい変数は、グローバルレベルで、「初期化せずに」宣言
・以下の記述のみの .def ファイルを、プロジェクトに追加する。

----------------- from here ------------
SECTIONS
_BSS READ WRITE SHARED
----------------- to here ------------

さて、先日、突然一日のキーストローク数をカウントしてみたくなりました。
公開するわけでも、販売するわけでもない、そういう目的なので、まあ、なんとか動かせばいいか……というレベルでいろいろ情報を探すと、

・システムフックというのを使えばいいらしい。
・システムフックは、dll として作成する必要があるらしい。
・この場合、dll は、各スレッドごとに作成されるので、グローバル変数(たとえば、キーストロークの総数)は、共有セグメントに持たなければならい。

ということ。
そして、共有セグメントを使う方法としては、

#pragma data_seg("セグメント名")
ここで、変数を宣言+初期化
#pragma data_seg()

としたあと、.def ファイルで、このセグメントを共有指定すればOKという情報が。

以上の情報を頼りに、dll を作り始めましたが、#pragma data_seg が変?
あ、これ、Visual C++ の独自拡張なのですね。BCB ではそのままでは使えません。

ではどうすればいいのか?
「メモリマップトファイルを使用してください」
あ、確かに、とっても正しい回答であります。

といいつつも、さらに、メモリマップトファイルまで理解するのはちょっとおっくう。
そこで、「何が本質的なのか?」と自問。

別に、変数に「任意のセグメント名をつける」ことが必要なのではなく、「その変数が存在するセグメントを、共有セグメントとして定義する」ことが必要なのです。

そう、「その変数が存在するセグメント」さえわかれば、(さらに、副作用さえなければ)、そのセグメント名を共有セグメントとして指定すれば良いわけです。

そこで、とりあえず作成したキーフックするソースをアセンブルしてみると、問題の変数は、BSS というセグメントに存在している。あとは、BSS というセグメントを共有セグメントにすればいいわけで……
※冒頭で、「初期化せずに」と書いたのは、たまたまこの条件の変数が BSS に存在していたというだけです。

keyHookdef.def というファイルを作って、その中に、上記の
SECTIONS
_BSS READ WRITE SHARED
を書き込み。

これをプロジェクトに追加すると、どうやら、所定の動作をしてくれたようです。
めでたしめでたし。

ちなみに、ソース全体は以下のようになります。

---------------- keyHookUnit1.cpp ------------------------
#include
#include
#include
#pragma hdrstop

// この下にある、グローバル変数が、BSS に存在するようです。
HINSTANCE hInst;
HHOOK hHook;
int pressCount;

static void save()
{
std::ofstream otf("c:\\user\\keyCount_LL.txt");
otf << pressCount;
otf.close();
}

static void load()
{
std::ifstream inf("c:\\user\\keyCount_LL.txt");

if (inf)
{
inf >> pressCount;
inf.close();
}
else
pressCount = 0;
}

LRESULT CALLBACK HookProc(int nCode, WPARAM wp, LPARAM lp)
{
if ((nCode == HC_ACTION) && (wp == WM_KEYDOWN)) // キー押下時
{
pressCount++;
if ((pressCount % 1000) == 0) save();
}
return CallNextHookEx(hHook, nCode, wp, lp);
}

extern "C" __declspec(dllexport) void HookStart()
{
load();
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)HookProc, hInst, 0);
}

extern "C" __declspec(dllexport) void HookEnd()
{
UnhookWindowsHookEx(hHook);
save();
}

extern "C" __declspec(dllexport) int get()
{
return pressCount;
}

extern "C" __declspec(dllexport) void clear()
{
pressCount = 0;
save();
}

#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
hInst = hinst;
return 1;
}
---------------- keyHook.h --------------------------------
#include

extern "C" __declspec(dllexport) void HookStart();
extern "C" __declspec(dllexport) void HookEnd();
extern "C" __declspec(dllexport) int get();
extern "C" __declspec(dllexport) void clear();

---------------- keyHookDef.def ---------------------------
SECTIONS
_BSS READ WRITE SHARED
---------------- to here ----------------------------------
posted by 麻野なぎ at 12:28| Comment(1) | TrackBack(0) | Twitter と Bot の周辺

2010年06月11日

OAuth への道 ―― OAuth gate 作ってみました

もしかして、Basic 認証の Bot の出力をそのまま、OAuth に置き換えられるんではないか……と、
ちょっと作ってみました。

非常に限定的な用途になりますが、もしかして、どこかに需要があるかも……しれないと。

【特徴】

・Bot と 同じパソコンの中で動きます(ネットワーク経由でも動かないことはない)
・故に、外部に、パスワードなどを送信しません
・ただし、パソコンの中に、OAuth 用のシークレットコードを保存します
・Twitter のパスワードは、(OAuth gate では)保存しません。

【対象】

・Windwos で動作します。(一応、OAuth gate が Windows 上で動けば、ネットワーク上の他のパソコンでBotが動くのも可です)
・POST にしか対応していません(Twitter API では、statsues/update のみです)
・何かを投稿する毎に、Twitter に「接続」と「切断」をするタイプのBotにしか対応しません。
・それ以外の機能が使われていたりすると、良くないことが怒ると思います。
・プロクシにも対応していません。
・ひとつの Bot に対してしか(というか、一組の、OAuth キーに対してしか)対応していません。

詳しくはこちらで → http://www.axis.blue/tec/oauth_gate.html
posted by 麻野なぎ at 12:31| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年06月10日

OAuth への道 ―― OAuth gate というのを考えてみた。

OAuth gate の動きを少々修正しました。
いや、修正したと言っても、まだ作ってませんが。
さて……。


Basic 認証で動く Bot と同じパソコンの中で動いて、OAuth 認証にして Twitter に投げるという発想で。

もともとが安易な性格なので

・Windows で動いて
・Bot のみ対応
・そもそも、POST のみ対応
・一回ごとに(認証だけではなく)接続と、切断を行うBotのみ対応

というパターですが。
こんなのでできるのかな?
とりあえず、○つけたのは、コードを書いたつもり。



○ 1)[SYSTEM] port の値(デフォルトは 80)で、サーバーとして待機する
○ 2)クライアントからの接続要求を受け入れる
○ 3)クライアントから切断が要求されたら、接続を切断する(自動)
×   ・その際、Twitter 側に接続中であれば、その接続を切断する

○ 4)接続完了後、クライアントからのデータを受信する
○ 5)クライアントからのデータは CRLF で1行ごとに区切る
○ 6)2個の空行(CRLF のみの行)を受信したら、次の処理に移行する

○ 7)最初の空行の直前までをヘッダとする。
○ 6)最初の空行の次の行をボディとする(ボディは1行のみ)
○   ・以上の結果を、Memo_server_recv に順次書き込む

8)ヘッダの処理
○   ・POST で始まり、.json を含む行 → POST http://api.twitter.com/1/statuses/update.json HTTP/1.1 に差し替える
○   ・POST で始まり、.json を「含まない」行 → POST http://api.twitter.com/1/statuses/update.xml HTTP/1.1 に差し替える
○   ・Authorization: で始まる行
×     Basic 以降、行末までの文字列を取得する
×     文頭・文末のスペースをトリミングする
×     得られた文字列のハッシュを算出する(MD5)
×     算出されたハッシュが、[USER] ID_Pass と一致することを確認する
×    ・一致しない場合、Twitter 側への送信は行わない
×      クライアントへは、とりあえず、エラーの旨を返す。
○     OAuth ヘッダを算出し、これに差し替える
○   ・Host: で始まる行 → Host: api.twitter.com に差し替える
○ ・その他の行 → そのまま
×   ・POST で始まる行、Authorization: で始まる行のいずれかが存在しない場合、Twitter 側への送信は行わない
      クライアントへは、とりあえず、エラーの旨を返す。

○   ・以上の変換処理(そのままの行を含む)結果を、Memo_twitter_send に順次書き込む

9)ボディの処理
○   ・ボディの内容を、そのまま、Memo_twitter_send に書き込む

× 10)api.twitter.com に接続要求を行う
× 11)接続を待つ → 60秒以内に接続できないとき、Memo_twitter_recv に、「接続エラー」を書き込み、サーバーとしての接続を遮断する
× 12)api.twitter.com に対して、Memo_twitter_send の内容を順次送信する
× 13)api.twitter.com に対して、読み込みを行い、読み込み内容を、Memo_twitter_recv に順次書き込むとともに、クライアントに送信する
× 14)受信終了文字列を受信したら( とか)Twitter への接続を切断する

○ 15)クライアントからの切断要求を受けて、接続を切断する。(3)と同じ
posted by 麻野なぎ at 12:20| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年05月19日

ツイッターのツイートはどこまで公開されているのか

この記事を書いている日、ツイッターでは、「無断フォロー」をきっかけとして、ちょっとした騒動があった。
その是非は置くとして、少なくとも、「自分のツイートを無断で見て欲しくない」という感情を抱きつつ、ツイッターを続けていたということなのだろうなと思う。

さて、この件については、類似の内容を「ツイッターの『嘘』」というタイトルで書いたばかりであるが、今一度、書いてみたい。

【ツイッターの一般的な印象】

どうも、インターネット上の「掲示板」などに比較すると、一般的な使い方では、ツイッターというのは、ずいぶんと「閉じた」環境に見えるらしい。
自分のフォロワーや、フォローを中心としたタイムラインという閉じた世界で、ツイートが連続するというイメージである。

だから、知らない人に勝手にフォローされる=知らない人に自分のツイートが読まれるのはちょっといやだな――という、そういう感覚が発生しうるのだろう。
しかし、考え方を変えれば、誰かをフォローするというのは、「ツイートを読みます」と意思表示をした上で読むということに過ぎない。
実際、そういった意思表示をしなくても、ツイートを読む方法というのは普通に存在するのだし、また、むしろ、自分が全く知らないところでも読まれ得るのだと意識しておくのは悪くないと思う。

【誰かのツイートを読むいくつかの方法】

たとえば、私自身、@asano_nagi のツイートを読むにはどんな方法があるだろうか。

ひとつは、 http://twitter.com/asano_nagi にアクセスするという方法である。これはあえて紹介する必要もないだろうと思う。
もうひとつは、 http://twitter.com/statuses/user_timeline/59402332.rss を RSSリーダーなどにフィードするという方法である。

これらは、特に珍しい方法ではない。そして、注目すべきは、ログインする必要も、そもそも、ツイッターのアカウントを持っている必要もないということである。

【もしかしたら、嗜好に関わる問題かも】

さらにいえば、ツイッターには、「おきにいり」という機能がある。
文字通り「おきにいり」だったり、後で読もうという目印だったり、それぞれに使われている機能だと思う。

いずれにしても、この機能を、「自分だけのメモ」として使っているというケースも多いのではないかと思う。
しかし、これも、たとえば、http://twitter.com/asano_nagi にアクセスしたあと、その場所で、「おきにいり」をクリックすれば、見ることができてしまう。
これも、ログイン不要である。
「自分自身のメモ用」として使うのか、「私としてはこれをおすすめします」という意味で使うのか、どちらを意識するかで、内容も異なってくると思うのである。

また、「ふぁぼったー」というサービスを経由すれば、さらに過去にさかのぼって「おきにいり」を確認することができる。

【終わりに】

以上書いたように、ツイッターのツイートは(そして、「おきにいり」も)普通に公開されているものである。
わざわざフォローしなくても、それどころか、ツイッターのアカウントを持っていなくても見えるものであると、そういう意識が、どこかで必要なのではないかと、そう思うのである。

※もっとも、ツイートを「公開しない」設定の場合、それは、きっと公開されていないのだろうと思う。
posted by 麻野なぎ at 21:08| Comment(0) | TrackBack(1) | Twitter と Bot の周辺

2010年05月18日

OAuth への道 ―― Twitter 専用 OAuth 認証モニタ(とっても簡易版)

Twitter における、Basic 認証の終了期限が近づいております。
そういうわけで、節気さんシリーズは、なんとか、OAuth 対応を終えたわけですが、もしかして役に立てばと思い、検討中に使用した、簡易型のモニタをアップしてみました。

oauth_mon.gif

ただし、もともと、単なる確認用に、やっつけ仕事で作成したものなので、statuses/update (単に、つぶやきを、ポストするだけ) しか対応していません
また、つぶやきとして設定する文を作成する手段も持っていません。
さらに、Proxy にも対応していません
それでも、もしも役に立つところがあれば

というわけで、実体はこちら → http://axis.blue/tec/oauthmon.html


posted by 麻野なぎ at 21:38| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年05月17日

OAuth への道 ―― OAuth はなぜ安全か(もうひとつ追加)

OAuth だと Basic 認証に比べてなぜ安全なのか? これは、以前に書いたのですが、書き忘れが当たったので追加します。
もうひとつ、OAuth 認証だと、「誰が書き込んだか」がはっきりわかるという点があります。
たとえば、あるアカウントから大量・無差別なメッセージが書き込まれたとします。
これは、誰がやったのでしょう?

もしかしたら、そのアカウントの正当な利用者かもしれません。もしくは、その利用者が利用しているどこかのサービスかもしれません。もしくは、パスワードが盗まれたのかもしれません。詐欺的なサービスに引っかかったのかもしれません。

従来、このような場合、「アカウント停止」という手段がとられがちでした。
そのアカウントの所有者が、本当に「やった」のかどうかが判断できないのと、それ以前に、SPAM を確実に止めるとしたら、そのアカウントを停止するしか方法がなかったからです。

Basic 認証では、「該当のアカウントのIDとパスワードだけを使う」仕様なので、これは同じ事です。
一方で、OAuth 認証が絡むと事情が変わってきます。
OAuth 認証で何かをしようと思うと、該当するアカウントに関する情報だけでなく、それを投稿する側の(たとえば Bot の)情報も必要です。
ですから、もしも、あるアカウントからSPAMが発信された際、少なくとも、「どんなアプリケーションを使ったか」は特定できます。

それが、たとえば、詐欺的なアプリケーションだった場合、(ユーザーのアカウントではなく)そのアプリケーションのアカウントを停止することで、ユーザーアカウントを有効にしたまま、SPAM を止めるということもできるようになります。

こういう利点もあるのでした。
posted by 麻野なぎ at 12:03| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年05月16日

OAuth への道 ―― C++で、OAuth認証(ただし、単純な Bot 限定)

さて、最終的に、OAuth 認証ヘッダを作成して、Bot として動作できそうなところに来ました。
実は、私が管理しているのは、「節気さんシリーズ」といいまして、単純に生成したメッセージを(とりあえず、メッセージを生成するまでの過程はそれなりに複雑)単純にポストするだけのものです。
ですから、Twitter へのリクエストは、POST http://api.twitter.com/1/statuses/update.xml のみです。(一応レスポンスのコードはチェックしていますが)

まず、非常におおざっぱに言いますと、Basic 認証であろうと、OAuth 認証であろうと、送信するヘッダ、ボディはほとんど同じです。
Authorization: Basic (Basic 認証ヘッダ)が
Authorization: OAuth (OAuth 認証ヘッダ)に変わるだけです。

というわけで、OAuth 認証ヘッダを生成できれば、Botとして動作可能というわけです。

----------- OAuth 認証ヘッダ作成プログラム -----------------
こんなのを作りました。

ヘッダファイル : create_oauth.h : http://www.axis.blue/tec/create_oauth.h
ソースファイル : create_oauth.cpp : http://www.axis.blue/tec/create_oauth.cpp

※上記のソースの中で、

#include <system.hpp>
#include <Classes.hpp>

の2行と、

void makeNonce() にある、 AnsiString time = Now().FormatString("yyyy/mm/dd-hh:nn:ss");
int UnixTime(TDateTime aTime) や、void makeTimeStamp() にある、Now() や、TDateTime 型は、Turbo C++ の処理系に依存しています。

別の処理系では、
AnsiString time = Now().FormatString("yyyy/mm/dd-hh:nn:ss");
は、単純に、time = "aaajajajajaja"; // ランダムな文字
とかで良いはずです。

時刻系のものは、なんとか、Unix Time を算出してください。(Visua C++ なら、CTime が使える気がします。

さらに、AnsiString も、処理系依存です。
こちらは、ほぼ、std::string で書き直せると思います。

これが、OAuth 認証ヘッダの生成に使うソースになります。

------------- 具体的にポスト ----------------------------

まず、単純に、abcd という文字をポストすることにします。
ボディは、 status=abcd です。

また、基本情報として、

OAuthDataDef mine =
{
"My_API_Name", // これは実際には使いません
"my_Consumer_key", // 実際には、入手した Consumer_key 以下同じ
"my_Consumer_secret",
"my_Access_Token",
"my_Access_Token_Secret"
};

と元になるキーを定義しておきます。
また、Body は、

char *body = "status=abcd";

とでもしておきます。

さて、このあと、

makeNonce(); // Nonce の生成
makeTimeStamp(); // timestamp の生成

AnsiString key = mine.Consumer_secret;
key += "&";
key += mine.Access_Token_Secret;
// ここまで、HMAC-SHA1 の鍵生成

AnsiString baseString = makeBaseString(mine, body);
// Base string を生成して

makeSignHere(baseString, key);
// シグネチャーを作って、

AnsiString oauthHeadder = makeHeadder(mine, body);
// OAuth 認証ヘッダを生成

となります。
create_oauth の内部構造が整理できていないので、呼び出しはこの順番でなければなりません。

※例によって、AnsiString は、処理系依存です。これも、std::string で置き換えられると思います。

ここまでで、OAuth ヘッダが生成できました。あとは、これを送信するだけです。
すでに、コネクションが成立したという前提で、(さらに、server->SendTo() でテキストが送信できるという前提で)

まず、"Content-Length: を生成します。

const char * CRLF = "\x0d\x0a";

AnsiString lenHeader = "Content-Length: ";
lenHeader += std::strlen(body);
lenHeader += CRLF;

で、

server->SendText("POST http://api.twitter.com/1/statuses/update.xml HTTP/1.1" CRLF);
server->SendText("Host: api.twitter.com" CRLF);
server->SendText("Content-type: application/x-www-form-urlencoded" CRLF);

oauthHeadder += CRLF;
server->SendText(oauthHeadder);
server->SendText(lenHeader);
server->SendText(CRLF);

server->SendText(line);
server->SendText(CRLF);

となります。
あとは、レスポンスを待つことになります。

【補足】

上記の例では、単純化するため、送信テキストを、"abcd" にしました。
しかし実際には、日本語を送信することが多いでしょう。
たとえば、
status=おはよう
であれば、UTF-8 で、URLエンコードしますから、実際に送るテキストは、

status=%E3%81%8A%E3%81%AF%E3%82%88%E3%81%86

になります。
シグネチャーを作成するための Base Text では、ここからさらに ERL エンコードされるので、Base Text の中では、
status%3D%25E3%2581%258A%25E3%2581%25AF%25E3%2582%2588%25E3%2581%2586
となります。

このあたり、最初ちょっと気がつきませんでした。
posted by 麻野なぎ at 11:06| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

OAuth への道 ―― Bot にとって、OAuth とは何だったのか?

OAuth については、種々解説もありますが、もともと、OAuth という考え方は、「いかに安全に第三者に自分のアカウントへのアクセスを許すか」という点を重視したものです。
ですから、実際のところ、「ユーザーの許可をどう得るか」という点に、多くの機能があてられています。

一方で、Twitter の Bot は、自作のものである限り、Bot の作者も、Bot のアカウントの管理者も同一だと考えて良いでしょう。つあり、はじめから、「ユーザー(この場合は、Bot のアカウント)の許可」はとれているということになります。
そのため、「Twitter で Bot として振る舞う」だけであれば、一般的な OAuth の解説のうち、かなりの部分を省略できます。

【OAuth の「一般的な」手順】

OAuth の解説の中で、一般的な手順は、たとえばこのように書かれています。
( APIアクセス権を委譲するプロトコル、OAuthを知る http://www.atmarkit.co.jp/fsecurity/special/106oauth/oauth02.html より、記述はかなり省略してあります)

1.コンシューマ登録
(1) コンシューマは、APIを通じたユーザーリソースの利用者としてサービスプロバイダへ登録を要求する。
(2) サービスプロバイダは、「コンシューマ・シークレット」をコンシューマに発行する。

2.リクエスト・トークン要求とユーザーの同意確認
(1) ユーザーはコンシューマのサービスを利用するために、Webブラウザでコンシューマにアクセスする。
(2) コンシューマはサービスプロバイダに接続して「リクエスト・トークン」の発行を要求する。
(3) サービスプロバイダは未承認の(Unauthorized)リクエスト・トークンをコンシューマに返す。
(4) コンシューマは、リクエスト・トークンにユーザーの承認を取るために、をサービスプロバイダにリダイレクトする。
(5) サービスプロバイダはユーザーにコンシューマがアクセスするユーザーリソースの内容と条件を表示し、同意の確認を促す。
(6) ユーザーの同意が取れたら、リクエスト・トークンを「承認(Authorized)」にして、コンシューマへリダイレクトして返す。

3.アクセス・トークン要求とAPI接続
(1) コンシューマは「アクセス・トークン」の発行を要求する。
(2) サービスプロバイダはアクセス・トークンと「トークン・シークレット」をコンシューマに返す。
(3) コンシューマはAPIに接続を要求する。
(4) サービスプロバイダはそれぞれのパラメータの値を確認し、問題がなければ、APIの応答データをコンシューマに返す。

これをすべてこなすのはかなり大変です。

【Twittter の Bot として振る舞う際に、最低限必要な手順】

1.コンシューマ登録
(1) コンシューマは、APIを通じたユーザーリソースの利用者としてサービスプロバイダへ登録を要求する。
(2) サービスプロバイダは、「コンシューマ・シークレット」をコンシューマに発行する。

3.アクセス・トークン要求とAPI接続
(1) コンシューマは「アクセス・トークン」の発行を要求する。
(2) サービスプロバイダはアクセス・トークンと「トークン・シークレット」をコンシューマに返す。
(3) コンシューマはAPIに接続を要求する。
(4) サービスプロバイダはそれぞれのパラメータの値を確認し、問題がなければ、APIの応答データをコンシューマに返す。

以上です。2.項が丸ごと抜けてしまいました。
これでもちょっと大変……という気がしますが、実運用は(Bot に限定すれば)それほどでもありません。

既に書きましたが、
(Bot 用、Access Token、Access Token Secret 入手法 http://asano-nagi.sblo.jp/article/37853895.html
Twitter にログインした状態で、簡単に入手できるわけです。

これをふまえてさらに書き換えると

【Twitter の Bot として振る舞う際に、最低限必要な手順 ―― Twitter にログインして】

1.準備
(1)コンシューマ登録をし、Consumer key, Consumer secret を取得する (上記「入手方法」の1.〜5.)
(2)その後、My Account Toke をクリックして、Access Token と、Access Token Secret を取得する。
   (上記「入手方法」の6.〜8.)

2.Bot として動作するとき
(1)OAuth 認証ヘッダを作成する。
(2)それを使って認証を行う。

以上に集約できます。

OAuth 認証ヘッダについては、次の機会に。
posted by 麻野なぎ at 11:04| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年05月15日

OAuth への道 ―― OAuth はなぜ安全なのか

さて、OAuth の具体的な対応方法は一時お休みして、そもそも、OAuth 認証というのが、Basic 認証に比べてなぜ安全なのか、とう安全なのかを書いてみようと思います。
一般的な解説記事などによれば、OAuth が安全なのは、「ユーザーのIDとパスワードを使わないから」といわれています。
確かにこの通りではあるのですが、OAuth では、その代わりに、Consumer key, Consumer secret, Access Token, Access Token Secret という4つの鍵を使用します。
そして、確かに、(ボットがそうするように)このキーを使って、ユーザーのかわりに様々な操作をすることができます。
さて、IDとパスワードなら危険で、同じように様々な操作をすることができる、4つの鍵なら安全だというのはどういうことでしょうか?

まず、何をもって「安全」とするかを考えなければなりません。
たとえば、従来の Basic 認証ならユーザーに成り代わっていろいろなことができるけど、OAuth 認証ではできることが限られるというわけではありません(ただし、実際には、「できることを制限する」という機能はあります)
また、一時話題になった、ダイレクトメッセージの機能を使って、SPMA のような拡散をさせるというものがありましたが、これも、OAuth 認証の元で動いていました。
ですから、ユーザーが、「承認した」状態では、機能的にはどちらであっても同じことできてしまいます。

では、何が「安全」なのでしょうか? それは

・ユーザーがいつでも(自分のパスワードを変更しなくても)承認を取り消すことができる。
・ユーザーがいつでも自分の都合でパスワードを変更できる。
・OAuth 認証では、ホストに送信される情報が盗まれても、それが流用できない(Basic 認証だとこれができてしまいます)

という点になります。

【ユーザーはいつでも承認を取り消すことができる】

従来のBasic認証では、それが、Twitter クライアントであっても、つぶやきの転送のようなものであっても、IDとパスワードを、連絡しなければなりません。(または、設定しなければなりません)
逆に言えば、何かのサービスを「もう使いたくない」として、絶対に使われないようにしようと思えば、パスワードを変更するという方法しかありませんでした。
新しいパスワードを知らない、クライアントなりサービスなりは、それ以降ユーザーのアカウントにアクセスできなくなります。
しかし、言い換えれば、そのほかの「まだ使いたい」サービスなどに対しても、パスワードを変更したと連絡する必要があるということです。

これに対して、OAuth 認証の枠組みでは、特定のサービスの「承認を取り消す」ことで、それ以降アクセスを禁止することができます。

【ユーザーはいつでも自分のパスワードを変更できる】

これは、前項とは逆の状況です。
たとえば、安全のために定期的にパスワードを変更する。または、パスワードが漏れたらしいので、変更する。
これも、従来の Basic 認証の枠組みでは、自分が使っているクライアントやサービスの事情をいちいち気にしなければなりません。

これに対して、OAuth 認証の枠組みでは、ユーザー以外のサービスはパスワードを使いませんから、いつでもパスワードを変更することができます。

【ホストに送信される情報が流用できない】

実は、「安全」という観点ではこれが一番重要なポイントです。
まず、インターネットでは、「暗号化」がなされていない限り、ホストに送信される情報は(受ける情報も)盗まれる可能性があるというのが前提となります。
ですから、Basic 認証であろうと、OAuth 認証であろうと、ホストに送信される「ユーザーのアカウントにアクセスするための情報」は、盗まれる可能性があります。

ただ、「盗まれた」情報が流用できるかどうかで、Basic 認証と、OAuth 認証の安全性に差が出ているわけです。

Basic 認証では、ホストに送信されるユーザー情報は、ユーザーのIDとパスワードから生成されるもので、いつでも同じものです。
つまり、一度盗まれてしまえば、何度でも(ユーザーが気づいてパスワードを変更するまで)利用できてしまいます。
これが、Basic 認証におけるセキュリティ上の問題点とされていたわけです。

一方で、OAuth 認証において、ホストに送信されるユーザー情報は、「アクセスの度に変化」します。
このため、ユーザー情報が盗まれたとしても、その情報は、2度と使えないという仕掛けになっています。

その仕掛けについて、少々書いてみましょう。

OAuth では、ホストに送信されるユーザー情報は、
「Consumer key」「Access Token」「付加情報」「署名」
です。
ここで注意したいのは、先に紹介した4つの鍵のうち、2つの鍵しか送信しないということです。
つまり、送信する情報が丸ごと盗まれたとしても、まだ、2つの鍵については秘密が守られるということです。

また、「付加情報」には、「Timestamp = 送信時刻」と「nonce = ランダムな文字列」が含まれます。
送信時刻が含まれるため、このTimestamp とかけ離れた(実際には、5分を超えたずれのある)時刻に受け取られたものは、ホストから拒否されます。
まず、盗んだ情報を元に、毎日アクセスするなどということはできないわけです。
さらに、「ランダムな文字列」というのは、単に、ランダムなだけではなく、「送信の度に異なる」ものでなければなりません。

送信時刻は、5分までのずれが許されました。といって、盗んだ情報をすぐに使おうとしても、今度は、「nonce = ランダムな文字列」として、「同じものが送信された」ということで、やはり、ホスト側から拒否されます。

したがって、もしも、この情報を盗んだとしても、2度とつかない情報だというわけです。

最後に、「署名」です。
この署名は、送信する、「Consumer key」「Access Token」「付加情報」の部分を、「鍵」を使って行います。
処理の内容は省略しますが、つまりは、署名に使う「鍵」を知っている人でないと署名を作成できないというものです。

さて、この、「鍵」になるのが、残った、Consumer secret, Access Token Secret の2つです。
つまり、この鍵を知らなければ、署名が作成できません。

前述の通り、「全く同じユーザー情報は2度と使えない」「異なる情報を作ろうとすれば、secret という名前のついた鍵が必要」「しかも、secret という名前のついた鍵は、ホストに送信されることがない」ということで、情報が盗まれたとしても安全だという仕掛けです。

【最後に――全部の鍵が盗まれたら】

以上のことを踏まえると、Consumer key, Consumer secret, Access Token, Access Token Secret が揃っていれば、不正アクセスでもできてしまうということになります。
一応、Consumer secret, Access Token Secret は盗まれることがない仕掛けになっているのですが、パソコンを丸ごと盗まれるとか、いろいろあるかもしれません。

実は、その場合でも、気づけば(というか、本当は、盗まれたことに気づくということが最も重要)アプリケーションの作者は、Consumer key, Consumer secret を変更できます。この時点で、盗まれた、Consumer key, Consumer secret は、無効になってしまいます。
しかも、Access Token, Access Token Secret も、「このアプリケーション専用」なので、他のアプリケーションから、盗んできた Access Token, Access Token Secret をもとに不正アクセスするということもできません。

このようなわけで、少なくとも、被害拡大は防げるようになっているわけです。
posted by 麻野なぎ at 20:43| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

OAuth への道―― HMAC-SHA1, Base64, URL Encode

-------------------- HMAC-SHA1 + Base64 でシグネチャーを作る -------------------------

さて、以前にも書いたとおり、この周辺の処理は理解できませんでした……で、ソースを
入手しました。

http://www.koders.com/c/fid645718469E4742143926FA6138FC7FD33BBD3A4F.aspx から入手しました。
使用したのは、base64.h base64.c hmac.h hmac_sh1.c memxor.h memxor.c sha1.h sha1.c です。


Windows の処理系で、かつ、C++で動かそうとすると、いくつかの変更が必要になります。
まず、

・各ソースに含まれている #inlcude は削除します。
 これは、unix 環境でファイルの依存関係などを記述するファイルらしいです。
 が、そもそも、config.h が入手できなかったのと、実際には、なくてもコンパイルできるので、これはあっさりと削除します。

・いくつかのソースにある、restrict を削除します。
 これは、C99 で定義された予約語で、関数の引数として渡された領域が「重なっていない」とコンパイラに仮定させるものです。
 標準の C++ では、まだ定義されてないので、これまた、あっさりと削除します。

さて、実際に、HMAC-SHA1 および、BASE64 でシグネチャーを作成します。

以上のファイルがそれぞれコンパイルできる状態で、

void makeSign(const char *inBuff, char *key, char *outBuff)
{
int inLen = std::strlen(inBuff);
int keyLen = std::strlen(key);
char resBuff[1024];

hmac_sha1 (key, keyLen, inBuff, inLen, resBuff);
base64_encode (resBuff, 20, outBuff, 64);
}

これで、inBuff から始まる文字列を、key という文字列で、ダイジェストし、Base64 で変換したものを、outBuff 以降に保持します。

ただし、実際に使用する際には、もう一段、URL Encode が必要です。

-------------------- URL Encode -----------------------------

URL Encode はそんなに難しい処理ではないので、自力で作りました。
もともと、inBuff と URL Encode して、outBuff にセットするというものです。
3番目の引数の、delChar は、別の用途に使い回したくて、つけています。
これは、URL Encode が必要な文字に対して、%xx とエンコードするのではなくて、「削除」します。
たとえば、

abc+def=ghi

を普通にエンコードすると
abc%26def%3Dghi

ですが、delChar = true; にして呼び出すと、
abcdefghi
とエンコードすべき部分を削除します。

これは、oauth_nonce に使い回すためです。
oauth_nonce は、「POSTごとにランダムな文字列」が要求されますが、ランダムな「文字列」を簡単に作るには、せっかくある、HMAC-SHA1 を使うのが早いと、そういう発想です。
実際には、
Base Strign = "2010/05/15-15:00:00" と時刻から生成し。
key = "1000000" → "1000001" → "1000002" と、POST ごとに+1した数値(を文字列にしたもの)
を使って、oauth_nonceを生成しています。
ただ、この中に、エンコードできる文字が入ってしまうと、ちょっと面倒なので、なら、エンコードできる文字は「削除」という発想でした。


void URLEnc(const char *inBuff, char *outBuff, bool delChar)
{
char *nonEscape = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

std::string line = "";
while(*inBuff)
{
char c;
char lBuff[16];
std::string orgLine = "";

c = *inBuff++;

if (std::strchr(nonEscape, c))
{
lBuff[0] = c;
lBuff[1] = '\0';
}
else if (! delChar)
std::sprintf(lBuff, "%%%02X", c);
else
lBuff[0] = '\0';

line += lBuff;
}

std::sprintf(outBuff, "%s", line.c_str());

}


こんな感じで、HMAC-SHA1 → BASE64 → URL エンコード と処理を進めることができました。
posted by 麻野なぎ at 15:27| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年05月14日

OAuth への道――具体的な送信内容

さて、Bot 限定でなんとか、必要なキーを入手することができました。
入手できたキー情報は4個
Bot の場合、キーの入手がかなりお手軽なので本当に助かりました。

Consumer key: aaaaaaaaaaaaaaaaaaaaaa
Consumer secret: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Access Token: cccccccccccccccccccccccccccccccccccccccccccccccccc
Access Token Secret: ddddddddddddddddddddddddddddddddddddddddddd

※このキーは架空のものです。もうちょっとそれらしいものをでっち上げたかったのですが……。

これをもとに、ヘッダを作成します。

必要な処理は

・URL Encode …… とりあえず自作。
・HMAC-SHA1 および、BASE64 によるシグネチャーの作成 …… ソースを取ってきました。
・Unix Time の算出 …… とりあえず自作。Turbo C++ にある、TDateTime 型から変換。

私の場合、「節気さんシリーズ」を動かすことが目的なので、とにかく、POST ができればいいという、そういう方針でまいります。

【HTTP ヘッダ】

POST http://api.twitter.com/1/statuses/update.xml HTTP/1.1
Host: api.twitter.com
Content-type: application/x-www-form-urlencoded
Authorization: OAuth oauth_nonce="777ccc777",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1273672200",
oauth_consumer_key="aaaaaaaaaaaaaaaaaaaaaa",
oauth_token="cccccccccccccccccccccccccccccccccccccccccccccccccc",
oauth_signature="xxxxxxxxxxxxxxxxxxxxxxxxxxx%3D",
oauth_version="1.0"
Content-Length: 135

※上記の oauth_nonce= から、 oauth_version="1.0" までは実際には一行です。(改行は入りません)

oauth_nonce="777ccc777",
―― ランダムな文字列です。このあとの、timestamp と併せて、リクエストの一意性を確保するためです。

oauth_signature_method="HMAC-SHA1",
―― シグネチャーの生成方式で、Twitter では、これに限定されます。

oauth_timestamp="1273672200",
―― リクエストを発行した時刻です。Unix Time とよばれるもので、
1970年1月1日00:00:00(UTC)からの通算秒です。
      この時刻から、5分間は有効らしいです。
      あとで第三者に「再利用」されないための処置です。

oauth_consumer_key="aaaaaaaaaaaaaaaaaaaaaa",
oauth_token="cccccccccccccccccccccccccccccccccccccccccccccccccc",
―― これは、既に入手しているキーになります。

oauth_signature="xxxxxxxxxxxxxxxxxxxxxxxxxxx%3D",
―― これが、シグネチャーでして、まあ、鬼門だと思います。

oauth_version="1.0"
―― これも、現時点では、1.0 に固定です。

【先に、HTTP Body の方を……】

Twitter に投稿するための、「投稿内容」です。
実は、シグネチャーの生成には、この内容も含まれます。

status=(UTF-8 で書いたものを、ERL エンコードした文字列)

です。

ここでは、簡単に

status=abcd

としておきます(これだと、実質的にエンコードの必要がない)

【シグネチャーの生成】

さて、いよいよ、シグネチャーの生成です。
シグネチャーは、「Base String」を、「key」によって、HMAC-SHA1 で変換したものを、さらに、BASE64 で変換したものです。

● Base String とは……

ここでは、POST 処理を考えていますので、

POST
&
http%3A%2F%2Fapi.twitter.com%2F1%2Fstatuses%2Fupdate.xml
&
oauth_consumer_key%3Daaaaaaaaaaaaaaaaaaaaaa
%26
oauth_nonce%3D777ccc777
%26
oauth_signature_method%3DHMAC-SHA1
%26
oauth_timestamp%3D1273672200
%26
oauth_token%3Dcccccccccccccccccccccccccccccccccccccccccccccccccc
%26
oauth_version%3D1.0
%26
status%3Dabcd

となります。
実際には、これを改行無しで1行にしたものです。
所々見えている、%3D は = を、%26 は、& をそれぞれ URL Encode したものです。
また、そのまま & になっている部分もあります(POST の直後と、http://api.twitter/1/statuses/update.xml の直後)

あと、順番にも注意が必要です。パラメータがアルファベット順に並ぶようにします。(この順番はそうなっています)

● Key とは……

ここまで使っていなかった、secret の出番です。
キーは、Consumer secret と Access Token Secret を & でつないだものです。
つまり、

bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&ddddddddddddddddddddddddddddddddddddddddd

がキーになります。

● これを、HMAC-SHA1 と BASE64 で整形する。

実際の処理は、理解できませんでした。
ソースを、 http://www.koders.com/c/fid645718469E4742143926FA6138FC7FD33BBD3A4F.aspx から入手しました。
使用したのは、base64.h base64.c hmac.h hmac_sh1.c memxor.h memxor.c sha1.h sha1.c です。

この例で計算すると、 lJdZXD19quOrpHsxMzfaM0WWkW8%3D になるようです。

急ぎ足ですが、まずは、一通りやってみたところで。
posted by 麻野なぎ at 23:54| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年05月10日

OAuth への道――Bot 用、Access Token、Access Token Secret 入手法

さて、Twitter の Basic 認証終了が迫りました。
節気さんシリーズは、現在 Basic 認証で動いているので、なんとか、6月中に OAuth に対応させなければなりません。
しかも、この Bot C++で動いているので、なんか、かなり自分でコードを書かなければならない雰囲気。
(とりあえず、HMAC-SHA1 の部分は、なんとかコードを探してくることにしますが……)

そこで必要になる情報が、アプリケーション(Bot プログラム)側で2つ。Consumer key と Consumer secret。
Bot のアカウント側で2つ。Access Token (oauth_token) と、Access Token Secret (oauth_token_secret) 。

前者の2個は、アプリケーション登録時に、その情報として得られるので良いのですが、後者の2個はちょっと難しい。
もともと、OAuth 自体が、アプリケーション(クライアント)側に、自分のパスワードを知られずに、機能を委譲するというものなので、アカウント側の情報を得るのは、ある程度面倒な使用になるのは仕方ないのですが。

ところで、Bot の場合、事情はかなり特殊です。
・そのアプリケーションは、Bot としてのポスト(など)にしか使用されない。
・Bot のアカウント自体も、もともと、プログラム作成者(=アプリケーションの登録者)が管理している。
ということで、やはり、Bot であれば、アカウント側の情報も簡単に入手する方法があったのでした。

1.http://dev.twitter.com/ を開く
2.(2)Register an app をクリックして、アプリケーションの登録(既に登録してあれば次に)
3.Bot のアカウントでsign in する
4.アプリケーションの登録作業
5.この時点で、Consumer key, Consumer secret が取得できる。
6.My Account Token をクリック
7.Access Token (oauth_token) と、Access Token Secret (oauth_token_secret) が取得できる。
 (同時に、該当アカウントの、設定 → 外部アプリ に、このアプリケーションが登録されている)
8.忘れずに、sign out

こんなところで、OAuth に必要な情報はとりあえず、すべて揃います。
あとは、これを、加工して認証まで持って行くことになります。

あとはまた。
posted by 麻野なぎ at 22:28| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年04月30日

Twitter の「嘘」

いえ、タイトルはちょっと過激につけてみましたが、それほどのものではありませんので、はい。

一般的に、Twitter を「使っている」立場のイメージは、

・気楽なつぶやきを投稿
・気に入った人があれば、フォローする。
・そうすると、フォローした人のつぶやきが「飛び込んで」くる。
・そうして、自分と、フォローした人たちのつぶやきが、「タイムライン」を流れていってしまう。。

こんなところだと思います。
さて、実は、自分の「つぶやき」が、かなり長い間保存されていて、しかも、だれにでも――Twitter のアカウントを持っていない人であっても――見られるということを、意識する機会は少ないのではないでしょうか?

実際、buzztter で検索することも可能ですし、google の検索にも引っかかります。
(自分で、検索することできます)

通常のブログであれば、ユーザーサイドでも、「書かれたものは消さない限り、保存・公開される」というのが、意識できる構造になっています。
一方で、Twitter は、「タイムライン」を見る限り、つぶやきは、どんどん流れていってしまう――と、そう感じられるのが、もしかしたら、その「つぶやき」が、永く・広く公開されてしまっているというのを、意識しにくい形になっているのではないかと、思うわけです。

※ただし、「自分のつぶやきを公開しない」設定であれば、一般的な検索には引っかかりません。

既に、「自宅を留守にします」とつぶやいたら、泥棒に入られたという事件も発生していますが、
http://www.ideaxidea.com/archives/2009/06/twitter_robery.html

もしかしたら、Twitter というのは、その、「感じ方」と「波及効果」の間に、かなりのギャップがあるのかもしれません。


【ここから、若干技術的なお話】

技術的には、「どんなデータを使うか」ということと、「そのデータをどう見せるか」というのは、個別に考えることです。
データは同じものであっても、どう見せるかで、掲示板のように見えたり、Twitter のように見えたりするわけです。

実際、技術的にいえば、Twitter に対して行われた「つぶやき」は、決して、「飛び込んで」は来ません。
自分が、フォローしている人のリストを頼りに(自動的にですが)読み込んでいるだけです。

だから、Twitter の方から見ると、ポストされるより、タイムラインを「読む」ことの方が、負荷がかかります。
ですから、多くの人にフォローされても、案外、システム的には平気なのですが、多くの人をフォローすると、システム的に結構きついことになります。(なので、フォローする数は制限がある)

このあたり、「どう見せるか」という点をよく考えられたシステムだと思います。
ただ、通常のユーザーサイドの感じ方を超えた、検索が可能なところは、若干、疑問を感じないでもありません。


posted by 麻野なぎ at 13:21| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

Twitter における Bot (API) のポスト数制限

さて、公式の資料(たとえば http://help.twitter.com/entries/15364 )によれば、

> Updates: 1,000 per day. The daily update limit is further broken down into smaller limits for semi-hourly intervals. Retweets are counted as updates.

となってます。
ここで、Updates というのが、実際には、ポストに相当するわけですが、1日あたり、1,000ポストまで、というのが見えます。
ただ、そのあと、The daily update limit is further broken down into smaller limits ... というのが書かれているのですが、ああ、broken down ―― 壊れる? もしかして、規制が無視される例外ケースがあるんだなと、まあ、1,000ポストなんてしないから、まあ、いいや、と、そう思ったのですが。

実際の投稿数規制は、 20ポスト/30分 なんだそうです。
そこで、冷静に和訳すれば

「一日あたり、1000ポスト。この制限は、同じ比率で、30分間隔で判断される」
ということで、1000ポスト/日 = 41.6ポスト/時 = 20.5 ポスト / 30分 となりまして、実際には、この数値でカウントされているということです。

実は、ここで動かしている Bot に、「50地域の日の出君」と「50地域の月の出君」というものがあります。
その名の通り、日の出・日の入り(そして、月の出・月の入り)の時刻に、つぶやくわけです。それもまた、 名前の通り、50地域を担当しています。

この設定だと、季節によって、短い時間にポストが集中してしまうわけですね。実際、2010年4月27日に、04:28:47 - 05:08:30 の約30分間に、25個のポストをして、そこで、制限に引っかかりました。

結局、同じような時刻のポストをまとめるようにして、この制限を回避しました。こんな感じ。

【旧バージョン】
4:28:47 《稚内》    日の出
4:34:52 《札幌》    日の出
4:41:35 《青森》    日の出
4:41:54 《盛岡》    日の出
4:45:29 《仙台》    日の出
4:46:00 《秋田》    日の出
4:47:35 《山形》    日の出
4:47:56 《福島》    日の出
4:50:08 《水戸》    日の出
4:52:13 《宇都宮》   日の出
4:52:48 《千葉》    日の出
4:53:25 《新潟》    日の出
4:54:14 《東京》    日の出
4:54:16 《さいたま》  日の出
4:54:53 《横浜》    日の出
4:55:45 《前橋》    日の出
4:56:28 《小笠原》   日の出
4:58:52 《長野》    日の出
4:58:54 《甲府》    日の出
5:00:41 《静岡》    日の出
5:02:40 《富山》    日の出
5:05:08 《金沢》    日の出
5:06:15 《名古屋》   日の出
5:06:27 《岐阜》    日の出
5:07:39 《福井》    日の出
5:08:30 《津》     日の出
以上 26ポスト/30分


【新バージョン】
4:29:00 《稚内》        日の出
4:36:00 《札幌》        日の出
4:42:00 《青森》・《盛岡》   日の出
4:45:00 《仙台》        日の出
4:46:00 《秋田》        日の出
4:48:00 《山形》・《福島》   日の出
4:50:00 《水戸》        日の出
4:52:00 《宇都宮》       日の出
4:53:00 《千葉》・《新潟》   日の出
4:54:00 《東京》・《さいたま》 日の出
4:55:00 《横浜》        日の出
4:56:00 《前橋》・《小笠原》  日の出
4:59:00 《長野》・《甲府》   日の出
5:01:00 《静岡》        日の出
5:03:00 《富山》        日の出
5:05:00 《金沢》        日の出
5:06:00 《名古屋》・《岐阜》  日の出
5:08:00《福井》         日の出
5:09:00 《津》         日の出
以上 19ポスト/30分


posted by 麻野なぎ at 13:03| Comment(0) | TrackBack(0) | Twitter と Bot の周辺

2010年04月18日

節気さん Bot の憂鬱

まあ、タイトルほど大げさな内容ではないのですが
まず、節気さん Bot の紹介から。
Twiter 向けに、そういう Bot を書いております。

詳しくは、
http://www.nagi.asano.name/twit_bot.html
をご覧いただけばと思います。

また、具体的につぶやいている内容は、
http://twitter.com/asano_nagi/sekki-series
のリストで確認いただけるかと。

さて、この Bot ですが、実は、Twitter のリアルタイム性に着目したものです。
もともと、二十四節気の該当日に、それをつぶやくということからスタートしたのですが、この、二十四節気、単に、「今日は(たとえば)夏至です」というだけではなく、「今日のxx時xx分が夏至です」という、時刻まで定義されているものです。
そういうわけで、その時刻に、「ただいま夏至です」というつぶやきをする、というコンセプトで始まったわけです。

そうすると、二十四節気の正確な時刻が必要なわけですが、これは、国立天文台の「暦要項」というページ
http://www.nao.ac.jp/koyomi/yoko/
で入手できます。

この Bot も当初、このページから入手した情報を元に、つぶやきのタイミングを決めていました。

さて、本日、二十四節気のタイミングを、Bot のプログラム内部で計算するように変更しました。
もっとも、内部で計算しましたといっても、本質的には、『日の出・日の入りの計算』(長沢工著、地人書館)の内容を流用しているわけですが。

この計算式は、非常に精度が高く、おおむね30秒くらいの精度で、二十四節気の時刻や、新月・満月の時刻などを算出できます。
それでも、数秒の誤差が、(つぶやくときの、時刻表示が分単位なので)見かけ上1分の誤差になることがあります。
結果的に、わざわざプログラム内部で時刻を計算して、それが、国立天文台の発表と異なるという結果になります。

ここで、苦労して自力で計算するよりも、天文台の発表をそのまま使った方が良いのではないか? むしろ、そのまま使うべきではないのか? と、そういう思いに悩まされるわけであります。

まあ、そう悩みながらも、やっぱり、「自分で計算してみたい」という欲求から、本日から、「自力計算バージョン」で出発するわけですが。

【時間区切りの怖さ】

さて、ここまでは、計算結果に数秒〜30秒以内の誤差があり得る、それが、見かけ上1分の誤差になって表れるというお話でした。
ただ、「時間区切り」というものがあるので、本当は、もっと恐ろしい誤差が出る可能性があります。
たとえば、冬至が、ある年の 12/20 25:59:58 などというポイントで発生するとしましょう。わずか5秒の誤差で、12/21 00:00:03 秒が、冬至であるという計算結果が出かねません。
そうすると、世の中より、1日遅れて、「今日は冬至です」というつぶやきをしかねないのです。

冒頭の、「1分の誤差」は、なんと言っても、「国立天文台の発表に対して、数分の誤差はあります」といって、認められる範囲だと思いますが、「今日は冬至です」が、翌日になってしまったら、ちょっと様にならないなぁと思うわけであります。

ま、ここ数年は、こんなシビアなタイミングはないようですが。

時間区切りという話では、関連して、「日本と中国における旧暦の日付がたまに、1日異なる」という現象も発生します。
これは、日本と中国の時差が1時間あることと、旧暦の「(その月の)1日は、朔(新月)の瞬間を含む日」という定義の関連です。
つまり、日本時間の午前0時から1時の間に朔の瞬間があると、中国時間では、まだ前日ということになります。
つまり、日本時間1月10日の午前0:30 が朔の瞬間であれば、1月10日が日本における旧暦の1日(ついたち)となります。
しかしながら、中国では、その瞬間1月9日なので、1月9日が、中国における旧暦の1日になるわけです。
さらに、ベトナムあたりまで範囲を広げると、旧暦の「月の決め方」の関係で、(旧暦における)新年が、一ヶ月近くずれると言うこともあるようです。
posted by 麻野なぎ at 16:49| Comment(0) | TrackBack(0) | Twitter と Bot の周辺