追記

高木浩光@自宅の日記

目次 はじめに 連絡先:blog@takagi-hiromitsu.jp
訪問者数 本日: 1782   昨日: 8921

2010年08月29日

三菱図書館システムMELIL旧型の欠陥、アニメ化 - 岡崎図書館事件(7)

21日の日記で示したMELIL/CS(旧型)の構造上の欠陥について、その仕組みをアニメーションで表現してみる。

まず、Webアクセスの仕組み。ブラウザとWebサーバはHTTPで通信するが、アクセスごとにHTTP接続は切断される*1。以下のアニメ1はその様子を表している。

アニメ1: HTTP接続とWebアプリのセッション管理
(クリックで開始、リロードでもう一度)

このように、アクセスが終わると接続が切断されて、次のアクセスで再び接続するのであるが、ブラウザごとに毎回同じ「セッションオブジェクト」に繋がるよう、「セッションID」と呼ばれる受付番号を用いて制御されている。

なお、赤い線は、その接続が使用中であることを表している。

次に、「3層アーキテクチャ」と呼ばれる、データベースと連携したWebアプリケーションの実現方式について。3層アーキテクチャでは、Webアプリケーションが、Webサーバからデータベースサーバ(DB)へと接続して、SQL文実行をDBに要求する。

以下のアニメ2は、3層アーキテクチャにおける、ブラウザからWebサーバへのHTTP接続と、WebサーバからDBサーバへのDB接続のタイミングを表している。

アニメ2: いわゆる「3層アーキテクチャ」の一般的構成(都度接続方式の場合)
(クリックで開始、リロードでもう一度)

このように、HTTP接続を維持したまま、DB接続でSQL実行を要求し、SQL実行が終わって、結果をブラウザに返すとき、DB接続とHTTP接続を切断する。このような実現方式は「都度接続方式」と呼ばれる*2

複数のブラウザからアクセスがあるとき、SQL実行が十分に短時間であれば、DBサーバ上でのSQL実行は同時に1つとなる(アニメ2の前半)が、あるSQL実行が長時間にわたった場合には、複数のSQL実行が同時に走ることになる(アニメ2の後半で青い丸が2個ある状態)。この数が過多となると、サイトが「重い」状態となる。

そして、メリル方式(旧型MELIL/CS)の場合を以下のアニメ3に示す。メリル方式では、セッションオブジェクトが作られると同時にDB接続が作成され、その後HTTP接続が切断されても、DB接続は維持されたまま、そのセッションオブジェクト専用として使われる。アニメ3では、DB接続が3本までに制限されている場合を想定している。(実環境ではその100倍前後の本数で運用されている。)

アニメ3: メリル方式(旧型MELIL/CS)の場合
(クリックで開始、リロードでもう一度)

このように、3台の端末のブラウザがアクセスしてきた時点で、DB接続の上限の3本を使い尽くし、4台目のブラウザがアクセスしてくると、DB接続に失敗して、そのブラウザは何度アクセスしてもエラー画面*3になってしまうという「閲覧障害」が起きる。(DBでの処理が「重い」かどうかに関係なく。)

そしてアニメ3の後半では、一部の端末がいなくなって10分が経過すると、セッションのタイムアウトが起きて、当該セッションオブジェクトが消滅し、消滅と同時にそのセッションが握っていたDB接続が切断されて、DB接続に空きができ、5台目の端末が接続できるようになる(閲覧障害が解消する)という様子を表している。

これがメリル方式の特徴であり、もし、永遠にDB接続が切断されないバグであったなら、毎日すぐに閲覧障害になって使い物にならないから、さすがに放置されることはなかったであろうが、このように、セッションタイムアウトまでの期間中に一定数のブラウザがアクセスに来たときだけ閲覧障害が起きるという、微妙なものであった。

次に、ブラウザがcookieオフ設定である場合をアニメ化してみる。

以下のアニメ4は、cookieオフのブラウザ、あるいは元々cookie機能を持たない自動アクセスプログラム(Googlebot等のWebクローラ等)が連続アクセスするときの様子を表している。

アニメ4: cookie非対応クライアントによる連続アクセス
(クリックで開始、リロードでもう一度)

このように、cookie非対応であることから、同じブラウザからのアクセスであることを識別できないため、次々と新しいセッションオブジェクトが作られて処理される。しかし、一般的には、Webサーバ上のセッションオブジェクトはほぼ無尽蔵に作成できるので*4、とくに問題はない。

次に、cookie非対応で都度接続方式のサイトに連続アクセスした場合の様子を、アニメ5に示す。

アニメ5: 都度接続方式におけるcookieオフでの連続アクセス
(クリックで開始、リロードでもう一度)

このように、アクセスごとにセッションオブジェクトは次々新しいものとなるが、「シリアルアクセス」でアクセスしている限り、DB接続とSQL実行は同時に1つであり、とくに問題は起きない。

ところが、メリル方式の場合はそうはいかない。cookieオフで連続アクセスするとアニメ6のようになる。

アニメ6: メリル方式におけるcookieオフでの連続アクセス
(クリックで開始、リロードでもう一度)

このように、3回アクセスした時点でDB接続が上限に達し、4回目以降はエラーとなる。そして、10分後には、セッションタイムアウトとなって、DB接続は解放され、元の状態に戻る。

岡崎図書館事件では、朝日新聞の記事によると「10分間にアクセスが約1千件を超えると、ホームページの閲覧ができなくなり」とあり、DB接続の上限が1000本だった。仮に1秒に1.5回のアクセスペースだとすると、10分で900本のDB接続を占有することになり、他に一般の利用者が100人以上いたならば、閲覧障害が生じることになる。もっとも、最初のアクセスから10分が経過すると、最初のアクセスで作られたセッションがタイムアウトし、DB接続が次々解放され始めるので、閲覧障害は解消されたはずである。

このことについて、「サーバ管理者日誌」の7月27日のエントリ「シリーズ・クロールとDoSの違いと業務妨害罪と(7) - 念力デバッグ再び」が、この閲覧障害発生のタイミングを図1のように推定していた。

グラフ
図1: 推定された閲覧障害のタイミング(「サーバ管理者日誌 2010年07月27日」より)
(ここではDB接続の上限を400本と仮定)

青い線は、連続アクセスが順調に進んだ期間を表しており、線が途切れている部分が、閲覧障害発生中の期間を表している。

このように、閲覧障害は、一旦起きるとずっと続くというものではなく、グラフのように断続的に、あるいは一時的に発生するものであった。その発生割合は、連続アクセスの速度と、DB接続の上限本数と、セッションタイムアウトの時間によって決まる(多くなったり少なくなったりする)。

ただ、アニメ3に示したように、一旦接続エラーになったブラウザ(アニメ3における4番目の端末のブラウザ)は、10分が経過した後も、DB接続のないセッションオブジェクトに繋がり続けるため、何度リロードしても閲覧障害が解消していないように見えただろう。この状態は、ブラウザを再起動する(あるいは、10分間以上リロードをしないで我慢して、タイムアウトさせてからリロードする)まで続く。

そのため、閲覧障害の発生期間がグラフのように部分的であっても、たまたまその期間中にサイトに訪れた利用者は、その後ずっとリロードしても閲覧できなくなることから、苦情の電話に至ったものと考えられる。

ちなみに、メリル方式のことを、DB接続を維持するものであることから、「コネクションプーリング」と呼んでいる人をTwitter界隈で稀に見かけたが、それは間違いで、こういうのをコネクションプーリングとは言わない。

コネクションプーリングとは、以下のアニメ7に示すような方式のことを指す。

アニメ7: コネクションプーリング方式(cookieオンの場合)
(クリックで開始、リロードでもう一度)

つまり、事前にDB接続を一定数確保して接続プールとし、Webアプリのセッションは、空いている任意のDB接続をプールから獲得して使用し、使い終えたら切断しないでプールに戻すというもの。都度接続方式に比べて、DB接続の確立に要す時間を省くことができ、レスポンスが早いと言われる。1990年代後半にその必要性が認識され、1999年ごろまでに確立し2000年ごろには広く知られていた

このようなコネクションプーリング方式や、都度接続方式を用いて3層アーキテクチャを構成していたならば、岡崎図書館事件の閲覧障害は起きなかった。これらの方式で閲覧障害が起きるとしたら、ひとつひとつのSQL実行の処理時間が相当長い場合*5、つまり、1つのSQL実行が終わらないうちに次のSQL実行が始まり、それが繰り返されて積み重なっていくような場合に限られる。「シリアルアクセス」ではそのようなことは起きないので、起きるとすれば、本当にたくさんの人が同時に訪れたときや、「パラレルアクセス」での真のDoS攻撃が行われた場合であろう。

21日の日記で示したように、2006年に開発された新型MELIL/CSでは、「都度接続方式」と「コネクションプーリング方式」が実装されている。

*1 Keep-Aliveについては、ここでは省略している。

*2 三菱電機ISの新型MELIL/CSの設定ファイルおよびコード上のコメントによると。

*3 旧型MELIL/CSでは、Global.asaにおいて、セッション開始時にのみDB接続を試みるように書かれていた。そのときエラーが発生しても、「On Error Resume Next」によって無視するように書かれていたため、初期化されない変数を抱えたまま、セッションオブジェクトは生成される。それ以降、そのセッションに接続するブラウザは、何度リロードしてもエラー「オブジェクトがありません」となる状態になったのではないかと考えられる。(エラー発生時にセッションを破棄するコードが書かれていなかったのならば。)

*4 無尽蔵にセッションを生成できるのは、Webが、HTTP接続をアクセスの都度切断するものとして設計されているため。もし、HTTPが接続を維持するタイプのプロトコルであったなら、セッション期間中のアクセス者数は、数百人ほどが限界になっていただろう。昔、Webが登場する前、Anonymous FTPをよく利用していたものだが、接続しようとすると「既に100人がログインしている」として接続を拒否されることがよくあったものだ。DB接続でも同様であり、使わない接続を維持し続けるのは無駄であり、3階層アーキテクチャでWebと結合すると、一般的なWebクローラが来ただけで破綻してしまう。

*5 旧型MELIL/CSでは、蔵書検索でのキーワード検索が異常なほど遅いという指摘も出ている。しかし、岡崎図書館事件で行われたアクセスは、蔵書検索ではなく、「TosCode」で指定された書誌ページへのアクセスであり、そのSQL実行は十分に短く、他の利用者への迷惑となるとは通常考えられない。

本日のリンク元 772 359 141 117 86 79 61 41 35 32 31 31 27 27 26 22 20 18 17 15 13 12 12 11 9 7 7 7 7 6 6 6 5 5 5 5 5 5 4 4 4 4 TrackBacks(9)

2010年08月24日

Macっ娘ならオートメータ君つかいたおすわよね

iPhoneといっしょにMacに乗り換える子が多いみたい。Mac買ったらまず開くのは「アプリケーション」フォルダなんだけど、左隅にいるちょっと気になるロボット君、「Automator」君っていうんだけど、知ってた?

画面キャプチャ

開くとこんな感じ。いろいろ並んでてよくわかんないけど、「インターネット」を選んで、「Safariの現在のWebページを取得」を選択。

画面キャプチャ

これを右のところにドラッグして、追加。こんな感じ。

画面キャプチャ

次は「WebページからリンクURLを取得」*1をドラッグして、その次に「URLをダウンロード」もドラッグね。

画面キャプチャ

こんな感じになるんだけど、もう完成。あとは、Safariで図書館サイトに行って、新着図書のページへGo!

画面キャプチャ

そしたら、Automator君の「実行」ボタンを押すだけ!

画面キャプチャ

リンク先のアドレスが抽出されて、

画面キャプチャ

もりもり取りにいって、はい終了。

画面キャプチャ

22ページ取りにいって19.277秒。けっこうアグレッシブ。ウェイトは1秒より短いみたい。

ばっちり取れてる。

画面キャプチャ

これが世界標準のMacの機能。5年前からあったみたい。クローラーキモいなんて言ってるコーダー女子もいるみたいだけど、パソコンくんばっか相手にしてると時代に取り残されちゃう。

至言だわ。

ちなみに、Automator君は /robots.txt は見ないみたい。見た目はロボットだけど。

*1 2006年の「Mac OS X 10.4.7 Update (delta) について」によると「Automator の「Web ページからイメージの URL を取得」アクションや「Web ページからリンク URL を取得」アクションで、Cookie やセッションを要求する Web サイト(特に、URL と共に認証情報が渡される場合)に対するサポートが、今まで以上に充実しました。」とのこと。

本日のリンク元 409 264 111 91 50 47 30 26 24 23 23 19 13 11 11 11 10 10 9 9 9 8 8 7 7 7 7 6 6 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 TrackBack(1)

2010年08月21日

Anonymous FTPで公開されていたGlobal.asaが示すもの 岡崎図書館事件(6)

もしや三菱電機ISのシステムはフリーソフトを使っていたりはしないかと、「WwKensaku.aspx」でググってみたところ(図1)、そこに現れたのは、 Anonymous FTPサイト専門の検索サイトだった*1。そこでさらに「WwKensaku.aspx」で検索してみると、なんとそこに現れたリンク先は ftp://210.230.245.201/ (図2)、このリンクをクリックすると図3の画面が現れた。

画面キャプチャ
図1: 新型MELIL/CSの検索ページURLのファイル名「WwKensaku.aspx」でググった様子

画面キャプチャ
図2: Anonymous FTPサイト専門の検索サイトでの検索結果

画面キャプチャ
図3: ftp://210.230.245.201/ のURLにジャンプしたときの様子

210.230.245.201 をDNSで逆引きすると sasaguri1.uxt.cknet.co.jp となる。http://sasaguri1.uxt.cknet.co.jp/ にアクセスしてみると、以下の図4のようになった。

画面キャプチャ
図4: http://sasaguri1.uxt.cknet.co.jp/ にアクセスした様子

ドメイン名が変だが、これは篠栗町立図書館(福岡県)の正規サイトのようだ。

Anonymous FTPサイトを見て廻ったところ、MELIL/CSのプログラム(VBスクリプト)の一部等が公開された状態になっていた。念のため、ftpコマンドでアクセスしてみたところ、「Anonymous access allowed」と表示され、パスワードなしに接続することができた(図5)。

画面キャプチャ
図5: ftpコマンドで接続すると「Anonymous access allowed」と表示された

公開された状態のMELIL/CSのプログラムのうち、特に重要なものは図6のディレクトリにあった。

画面キャプチャ
図6: /Contents/TOSHOW/ の一覧

ディレクトリを隈無く見て廻ると、「Global.asax」が置かれているように、これは新型の方(ASP.NET版)であるが、旧型(ASP版)の残骸のようなもの*2が随所にあり、旧型のものと思われる「Global.asa」が置かれていた。

その中身は図7のものであった。

画面キャプチャ
図7: 旧型MELIL/CSの残骸と思しき「Global.asa」

肝心の部分のコードは以下のとおりである。

Sub Session_OnStart
(中略)

'エラー対策?
    On Error Resume Next

'図書館用データベース
    Set Session("OraDatabase") = OraSession.DbOpenDatabase(HostName ,LoginPasswd ,Clng(3))
End Sub

Sub Session_OnEnd
    Set Session("OraDyna_Book") = Nothing
    Set Session("OraDatabase") = Nothing
    Set Session("OraSQLStmt") = Nothing
End Sub

まさに、Twitter界隈で皆が予想していたとおりだった。

つまり、7月14日の「サーバ管理者日誌」で「念力デバッグ」として、現象から推測されていたことがほぼ図星で、7月22日の「その後の念力デバッグ」で補正されたものがズバリであった。そして、この推測を基に再現実験が行われた「ぐて〜blog」の7月24日のエントリ「岡崎市立図書館の件のサーバ側挙動の擬似環境下再現実験(その2)」で作成された「Global.asa」は、

Sub Session_OnStart
	Set Session("OraDatabase") = Application("OraSession").DbOpenDatabase("libradb","scott/tiger",clng(3))
End Sub

Sub Session_OnEnd
	Set Session("OraDatabase") = Nothing
End Sub 

というもので、さらに、中川氏が警察の取り調べで見せてもらったというサーバ側アクセスログにあったエラーメッセージの記憶を基に、そのようなエラーメッセージが出るよう、「On Error Resume Next」のエラー処理を追加したのが、7月29日の「ASP実験、Session_OnStartのオブジェクト生成失敗を無視する処理」のときの「Global.asa」であり、まさにその推測は完全に当たっていたということになる。*3

このコードの場合、DB接続はアクセス元のブラウザ専用として確保(ASPセッションに紐付け)され、ASPセッションがタイムアウトするまでの間(7月の時点で岡崎市立中央図書館では実測で620秒くらいだった)、閉じられることなく(使いもしないのに)占有されてしまう。その間に新しい閲覧者(ブラウザが)接続してくると、次々新しいDB接続が確保され、その最大数に達すると、それ以上のアクセスを受付けなくなってしまう。

旧型MELIL/CSの設定ファイルもあった(図8)。

画面キャプチャ
図8: 旧型MELIL/CSの設定ファイル

2003年8月に作成され、少なくとも2005年7月まで正式に活用されていたことが窺える。

iモード版ではcookieが使えないため、Global.asaによるASPセッション中のDB接続の維持ができないことから、アクセスの都度接続する方式をとっているらしく、DB接続のID・パスワードがこの設定ファイルに書かれている(Gloabl.asaではなく)。

他にも管理者用のものと思われる「AdminTools.asp」が公開された状態になっていた。これは旧型であり、2003年5月に開発されたことが窺える。

画面キャプチャ
図9: 「AdminTools.asp」が公開された状態になっていた様子

ここで謎なのが、著作者として個人の氏名が書かれていて、およそ会社の職務で書いたものには見えない文言が書かれている。旧型の.aspに著者名があるものは、どれもこの方によるものになっていた。*4

一方、新型(ASP.NET)の方はどうなっていたかというと、まず、「Global.asax.vb」にはDB接続するコードは書かれていなかった。そして、新型用の設定ファイルに以下の記述があった(図10)。

画面キャプチャ
図10: 新型(ASP.NET版)MELIL/CSの設定ファイルにある記述

「データベースのアクセス方式を指定する」とあり、「0:都度コネクト」と「1:プーリング機能」が選択できるようになっている。

このDB接続の実装は、「Database.vb」にあった(図11)。

画面キャプチャ
図11: 新型(ASP.NET版)MELIL/CSのDB接続の実装コード「Database.vb」の肝心部分

これを読むと、新型のMELIL/CSでは、今回の岡崎市立中央図書館のような事態や、他の各地の図書館で報告されていた不具合(/robots.txtを置かないと一般のクローラが来ただけで支障が出てしまう)が生じないよう、ちゃんとDB接続の管理が作り込まれていることがわかる。「都度接続方式」では、SQL文の実行が終了してASPページの実行が終了する(HTTPレスポンスがブラウザに返される)までに、毎回DB接続を閉じるように書かれている。

このコードが書かれたのは、2006年3月〜2007年2月であり、少なくともこの時点で三菱電機ISは、旧型の欠陥を原因まで含めて承知していたはずと言える。

そして、このAnonymous FTPサイトの存在を朝日新聞の記者*5に通報したところ、数日後にはこのAnonymous FTPは閉鎖されていた。

*1 この時点で「何これ?」とTwitterにつぶやきそうになったが、後になって思えば、思いとどまってその先まで調べてよかったと思う。

*2 もしくは、部分的に旧型のプログラムを流用している、あるいはテスト用ページ(旧型の)を残したままにしているために、旧型のコードの一部が置かれていたと考えられる。

*3 私が朝日新聞からの依頼を受けたのはそれより後。

*4 一方、新型の .vb のコードには、別の方の名前がローマ字で記載されていた。

*5 7月16日に、情報ネットワーク法学会のこの事件をテーマにした座談会に取材にいらしていて、お目にかかっていた。

本日のリンク元 1640 768 727 686 638 469 400 378 324 315 213 195 127 123 89 76 68 65 63 58 51 50 45 42 42 39 39 38 32 31 30 29 28 28 28 27 27 27 22 19 18 17 17 17 17 17 15 14 12 11 10 10 10 10 10 10 9 8 8 8 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 TrackBacks(8)

追記

最近のタイトル

2010年08月29日

2010年08月24日

2010年08月21日

2010年08月11日

2010年08月09日

2010年08月08日

2010年07月10日

2010年07月03日

2010年07月02日

2010年06月19日

2010年06月05日

2010年05月30日

2010年05月29日

2010年05月20日

2010年05月16日

2000|01|
2003|05|
2004|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|
追記