LoveLangの熱き戦い

2週ほど前に私はLoveLangという
小さなサイトをHeroku上に公開しました
ちょうどWebSocketを学んだばかりだったので
ちょっとデモを作ってその動作を試してみたかったのです
いつものようにそのことを
このブログとTwitterで告知しました


何人かの人が来て
自分の好きな言語のボタンを数回クリックして
去って行きました
中にはそこに留まる人もいました
LoveLangはリアルタイムで見ている人の人数がわかるのです


暫くは大した動きはなかったので
あらためてTwitterで投票を呼びかけてみました
これにfollower1800の@jugyoさんが答えてくれました*1
するとTwitter上にLoveLangのリンクを貼ったTweet
増殖していきました
10前後だったUserCounterの値が30 40と増えていきました
円グラフ上の数値はすごい勢いでインクリメントしていきました
これを機に様相は一変したのです


遂にはその声は
follower15000のRubyのパパにも届きました*2
UserCounterは100を超えました
TwitterでもLoveLangを話題にしたつぶやきが多数見られました
LoveLangはプログラマの何かに火をつけたのです


だんだんサイトが重くなって
新たなコネクションを張れなくなりました
そしてfollower45000の@dankogai氏が訪れたとき*3には
コネクションは300を超えて
さらに接続は困難なものになりました


私は興奮しました


円グラフの数値はすごい勢いで増えていき
各言語の値があっという間に
10万クリックを超えました
Twitter上で特定の言語に対するクリック支援
を要請する動きすらありました
そのような口コミの広がりが新たな参加者を呼び込み
グラフ上の数値は加速度的に増加していったのです


サイト情報:lovelang.heroku.com - Ceron.jp
Twitter Trackbacks for Love Languages?


しかしその中には明らかに人間の手に拠らないすなわち
スクリプトによる値の増加が見受けられました
Twitter上でもこれに言及する発言が増えていきました
LoveLangを「公認DoSサイト」とか
「自動クリックゲー」と呼ぶ人も現れました
考えてみれば
怠慢・短気・傲慢を美徳とするプログラマ
「マウスをクリックして言語愛を示せ」とは
無理な相談だったのかも知れません


ここに至って私はやっと
自分が一部のプログラマ
挑戦状を叩きつけていたことを知るのです...


深夜になっても
Clicked by Scriptは止まりませんでした
サイトはWebSocket実現のために
Pusher*4の無料プランを利用しています
しかしどういうわけかその制約を超えてコネクションは維持され
無数のClicked by Scriptをさばいたのです


やむなく公開から8時間後一旦サイトを閉鎖し
データをリセットしました
腱鞘炎を顧みずコーディングも放り投げて
ひたすらクリックで言語愛を表明してくれた人たちには
申し訳ない気持ちで一杯でした..


その日のメッセージ総数は
信じられないことに1億に達していました
軽い気持ちで公開したデモサイトが
自分に未曾有の経験をもたらしました
私にはまともなサイトを公開した経験も
サイトセキュリティに関する知識も
頼れるプログラマの知人もいなかったので
ほんとうに戸惑いました


深夜の連打祭り 〜 lovelang.heroku.com 〜 - Togetter



しかし有難いことに
Twitter上で対策の糸口となるつぶやきを見つけました

みなさんもChromeのコンソールで for(i=0;i<10000;i++){document.getElementById('5').click();} しましょう


そう犯人は現場に戻る習性があるのです!


なぜ犯人は現場に戻るんでしょうか? - Yahoo!知恵袋


早々私は対策に乗り出し
$("button").click を
$("button").mouseup に変更して
再公開に踏み切ったのです
公開するやいなやコネクションは数十に達しました
暫くは対策の効果が効いて
Clicked by Scriptは静かになりました


ところが1時間もしないうちに
連続する高速のカウントアップが始まり
あっという間に値は10万に達しました
私は数時間でまたサイトの閉鎖を余儀なくされました


私はまたヒントを探して
Twitter上のつぶやきを見守りました
そして「Ajaxのpostコールがidだけで行われている」
旨の発言を見つけました
サイトでは言語ボタンをクリックすると
そのボタンのidと共に
Ajaxのpostリクエストが発動してサーバ側に渡ります
サーバはそのidから
対応言語のカウンタをインクリメントしてPusherに渡します
PusherはそれをWebSocketを介して
各Browserにブロードキャストするのです


Clicked by Scriptが行われているときに
postリクエストのend pointを変更してみると
果たしてカウンタはストップしました
マウスクリックをシミュレートする
スクリプトではこうなりませんから
これで犯行の手口がはっきりしました


私は早々対策に乗り出しました
WebSocketではBrowserのタブ毎に
固有のソケットIDを割り当てるので
postリクエストにこのIDを共に渡すようにしました
またend point側でunless request.xhr?の記述を追加して
postリクエストをAjax限定にしました


サイトを再開すると
すぐに数十のコネクションが張られましたが
Clicked by Scriptは止みました
私はこれでLoveLangに平和が訪れることを期待しました


しかしその期待はすぐに裏切られることになりました
暫くするとpostリクエストに
ソケットIDを渡すscriptが書かれ
私の防潮堤は簡単に破られました
またしても私の認識は
全然甘かったことを思い知るのでした


私はサイトを閉鎖し再度対策を検討しました
そしてpostリクエストにsessionデータを含む
いくつかの固有データを共に渡すようにしました
sessionデータはAES 256で暗号化し
改ざんされた場合はリクエストを拒否するようにしました


またリクエストのend point名を
「clickbyyourfingertipplease.json」に変え
サイトには「run is not love, but click is.」と表示し
更にはTwitterで「少し愛して、長く愛して」とつぶやいて
その良心に訴えるように努めました.. ^^;


そしてそのどれかは一定の効果を奏しました :)
postリクエストを偽装した侵入は激減しました*5


しかしその一方で別の言語に対する
別の手口による犯行が始まりました
今度はpostリクエストのend pointを変えても
カウンタはストップしません


マウスクリックのシミュレートに対しては
clickイベントをmouseupイベントに変えた
だけの対策しかできていませんでした
そこでカーソルがあるべき場所にあるときだけ
mouseupイベントが発動されるようにしました


この対策も一定の効果はありましたが
それが効かないスクリプトもありました
java.awt.Robotのような
カーソル自体を制御するライブラリを使えば
人間のマウス操作をシミュレートできるからです


ここに来て私は
自分の実力とサイトセキュリティの限界を見ました


「どんなに高い壁を作ってもそこを乗り越えてくるやつはいる」
3.11の教訓が私には全く生かされていませんでした..




そこで視点を変えてみることにしました
Clicked by Scriptを受け入れることにしたのです
一方で
一定時間内のクリック数を制約するルールを入れたのです
この制約はもちろん
真面目にクリック(?)している人にも働きますが
人間はこれに対応できますし
彼の腱鞘炎を防止するためにも適切な措置なのです





2週間が過ぎて
LoveLangにはかつての熱狂はなくなりました
それでもLoveLangをちょっと覗いて
自分の愛する言語にクリックしていく人がいます
この1週間は平穏な運営が続いています*6


今となっては
私が施した幾つかの対策が功を奏しているのか
それとも
「自動クリックゲー」に彼らが飽きたのかはわかりません*7
「lovelangとは何だったのか」などと
あの熱狂を振り返って哲学的なTweetをする人もいました


昨日こんなうれしいTweetを見ました

今宵はマッカーシー教授を偲んで#lovelangでもするか.*8


LoveLangは今も生きています


昨日 言語関連Tweetを表示するリニューアルを施しました
これからも
LoveLangをどうぞよろしくお願いしますm(__)m


Love Languages?


melborne/LoveLang - GitHub