MongoDB (2)
今年のノーベル文学賞は、春樹に変わってイシグロさん。春樹は何時も候補に挙がって沸かせて くれるけど、幸運の女神さんは微笑んでくれない。微笑むのは、あの国の賭け好きの会社なり。
ミーハーな女房は、早速図書館の蔵書検索に走る。そして絶望を知る。リクエスト待ちが数十人 ですって。諦めて、本屋に走るが、影も形も無しの状況。
穴馬、大穴なんで、早川書房さんは何も用意してなかったんだな。春樹本は発表前から山積みに なって、当選を狙っていたのにね。
日本は昔から外圧に弱い。外国で絶賛されたものをありがたがる習性が染みついているな。 自分でどうこうしようと言う態度無し。
この間、本屋に行ったら、山積みされてた。商機を逃してなるものかとばかり、大増刷されて た。みんな文庫本。ハードカバーで、これ見よがしに飾ろうとした人は、あてが外れましたな。
女房も、一冊買ってきた。読み始めた所らしいけど、春樹よりは読みやすいとの事。
何処かの出版会社の社長が言ってたな。図書館は文庫本を買わないでくださいと。オイラーも そう思うぞ。安く読めるんだから、自分で買えよ。図書館はそんなに、民衆に迎合する必要無し。出版業界を応援してあげなさい!!
オイラーは、時代遅れと言うか、名作をゆっくり読んでいる。陸王みたいにタイアップした やつは興味無し。
たまたま、NHKのサラ飯で出て来た、故米原真理さんに興味を覚えて、遅ればせながら手に してみた本がある。『噓つきアーニャの真っ赤な真実』って本。題名から、なんかおちょくって るなと、思ったけど、これ、オイラー的には、大当たり。チェコの学校で机を並べた級友を 20年だかたってから、訪ね充てる物語が3編収録されているんだけど、よくもまあ探し出したなと、感心しきり。体験談だけに、リアリティーが有って、楽しく、そしてちょっぴり悲しい 部分もあって、楽しめましたよ。
面白いので、別の本も読んでみるかな。
binary
前回は、hexeditなんて道具を振り回したものだから、面白いバイナリーものの話は無いものだろうかと、探してみたんだ。そしたら、
就職面接でプログラムの解読を求められた! なんてのに出くわした。ぐぐるの試験を彷彿させるな。一種の暗号解読。知恵と忍耐。
暗号がらみで、 たのしいXOR暗号入門なんてのも。Haskellて指定した訳でもないのに、Haskellが出てくるのは、そういう方面に 向いているから?
暗号解読と言うと、解読して平文のメッセージが出てくれば成功って事で、解説されてる。 平文がbinaryの場合は、どうするねん?
簡単な話で言うと、helloをgzipで圧縮。これで平文はbinaryになる。このbinaryに暗号化を する。さあ、これを解読して、元の平文、helloを復元してみろって訳。
gzipで圧縮されたbinaryは、痕跡が残る。
$ echo hello world > aa $ gzip -c aa > xx $ file xx xx: gzip compressed data, was "aa", last modified: Fri Nov 10 14:44:40 2017, from Unix
暗号をデコードする度に、fileコマンドにかけて、何か出てくるか観察する手口が考えられる。 で、このファイルコマンドのネタ帳がmagicというファイルになってるんで、注目点を 洗い出してみる。(邪悪なLinuxでは、このネタ帳がバイナリーになってる。まるで、 binary大好きな某OSみたいだな。)
$ grep gzip /etc/magic : # gzip (GNU zip, not to be confused with Info-ZIP or PKWARE zip archiver) 0 string \037\213 gzip compressed data
そうなってるか、確認。
$ hexdump -C xx 00000000 1f 8b 08 08 58 bb 05 5a 00 03 61 61 00 cb 48 cd |....X..Z..aa..H.| 00000010 c9 c9 57 28 cf 2f ca 49 e1 02 00 2d 3b 08 af 0c |..W(./.I...-;...| 00000020 00 00 00 |...|
この痕跡を消さないと、容易に推測されちゃうな。はて、どうする?
色々手を打てる。アイデア勝負。木は森に隠せとか、切り刻んで、ミンチにしちゃうとか。一度 暗号化しちゃうとか(上の例だと、xxってファイルのSHA512ぐらいでハッシュを取り、それをRC4のキーにするとか)。そういう手を使ったものを、どう解読するかCIAの人に聞いてみたいものだ。
gzip
上でgzipが出てきたので、少し調べておく。
fileコマンドを使った時、圧縮前のファイル名と作成日時が出てきた。それって、ファイル コマンドがネタ帳を元に少し解析してくれたの? 上のダンプを眺めると、確かにファイル名が 埋められているのが観察出来る。よって、ファイル名から足がつかないように、痕跡は消しておけ。
日時は、多分1970年からの経過秒になってると思われるけど、ちと、何処から始まってるか、 目視では分からない。こういう時は、ソースに当たるに限る。
ほとんどの機能をlibzに負っている。本体がどうこうより、ヘッダーファイルに注目だな。 libzと対になるのが、/usr/include/zlib.h
/* gzip header information passed to and from zlib routines. See RFC 1952 for more details on the meanings of these fields. */ typedef struct gz_header_s { int text; /* true if compressed data believed to be text */ uLong time; /* modification time */ int xflags; /* extra flags (not used when writing a gzip file) */ int os; /* operating system */ Bytef *extra; /* pointer to extra field or Z_NULL if none */ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ uInt extra_max; /* space at extra (only when reading header) */ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ uInt name_max; /* space at name (only when reading header) */ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ uInt comm_max; /* space at comment (only when reading header) */ int hcrc; /* true if there was or will be a header crc */ int done; /* true when done reading gzip header (not used when writing a gzip file) */ } gz_header;
親切な事に、参照すべきRFCまで載せてくれている。 RFC1952
このRFCとヘッダーを見比べてみると、場合によってはファイル上で存在しないメンバーも 有る事が分かる。ただ、osの種類は、RFCの文書から容易に知れる事は良い事だな。(場合に よっては、あちこちをつつかないと、判明しない事が有るからね。)
OSの種類と更新日を、変更してみるかな。hexeditの出番だな。
$ file xx xx: gzip compressed data, was "aa", last modified: Mon Jul 13 10:08:00 1970, from NTFS filesystem (NT)
こんな昔に、マイクロソフトなんて有ったっけ? ゲイツはかーちゃんの乳を吸ってたかもな? ああ、彼は1955年生まれだったから、かーちゃんじゃなくてGFの乳って事もあり得るな。ねーちゃんのケツばかり追いかけていたら、Windowsは生まれなかったかもよ。
大げさにソースを引っ張り出したり、RFCを参照までしてみたけど、もう少しカジュアルな方法が有る。上で挙げた、magicをじっと見つめる方法。
>4 ledate >0 \b, last modified: %s >9 byte =0x00 \b, from FAT filesystem (MS-DOS, OS/2, NT) >9 byte =0x01 \b, from Amiga >9 byte =0x02 \b, from VMS >9 byte =0x03 \b, from Unix >9 byte =0x04 \b, from VM/CMS >9 byte =0x05 \b, from Atari >9 byte =0x06 \b, from HPFS filesystem (OS/2, NT) >9 byte =0x07 \b, from MacOS >9 byte =0x08 \b, from Z-System >9 byte =0x09 \b, from CP/M >9 byte =0x0A \b, from TOPS/20 >9 byte =0x0B \b, from NTFS filesystem (NT) >9 byte =0x0C \b, from QDOS >9 byte =0x0D \b, from Acorn RISCOS
4バイト目からのデータが0以上だったら、ローカルの時間で表されて更新日時。冒頭の>(複数の場合有り)は、gzipと判明した後に行われる検査レベルを表す。9バイト目は、それぞれ一意のフラグを表す。アミガだとかVMSだとか、亡霊が潜んでいるな。
$ file xx xx: data
冒頭の2バイトを破壊してみた。fileコマンドのネタ帳にも載っていない形式になっちゃったんで、データですって、当たり前の事しか言わなくなった。忍法隠れ見の術だな。
ああ、上で痕跡を消すのに、ハッシュを取っちゃえって案をあげたけど、これは却下だな。 こんな事をしたら、誰も解読出来なくなっちゃう。
どうしても元ファイルを軽く暗号化したいなら、ファイル長を求める。それを漢数字に直す。 (中国人は読めちゃうから、かなもしくはカタカナがいいかな)文字コードは、時代遅れの EUC-JPなりISO-2022-JPを使う。そしてそれをsha512ぐらいにして、キーを得る。 平文ファイル自身をキーにするしかけだ。簡単にshell語で組めると思うので、やってみれ。
ああ、shell語は汎用だから、haskell語がいいか。
頭を捻ってコードを起こすのもいいけど、unix屋さんは有るものを貼り合わせて、作りあげて しまうのが流儀。暗号化なら直ぐにSSL一族を思い出すな。でも、これって普通にWindowsやリナに入ってるでしょ。BSD系はどうよって調べてみると、FreeBSDにエニグマが入っていた。
ドイツ軍の有名な暗号装置。これを模したもんが公開されてる。
[fb11: tmp]$ echo hello world | enigma > bb Enter key: [fb11: tmp]$ hexdump -C bb 00000000 91 f2 f3 e6 66 b3 f6 ae 5a 38 eb 18 |....f...Z8..|
ハロワを暗号化してみた。確かに、ばらけているな。
[fb11: tmp]$ enigma myKey < bb hello world
そして、復号化。キーをコマンドラインから与えるという、無防備な事をしちゃった。 bashのヒストリーを見れば、一目瞭然じゃん。
エニグマはチューリングにより解読されるはめになった。今なら容易にコンピュータの馬鹿力で 解読出来ちゃうだろう。そういう心配性な人は、 bdesを使うと、少しは安心出来るぞ。
[fb11: tmp]$ echo hello world | bdes > z Enter key: [fb11: tmp]$ hexdump -C z 00000000 96 9c c5 cc 1e fd 44 6b ec 02 d2 28 f7 50 0c d2 |......Dk...(.P..| [fb11: tmp]$ cat z | bdes -d Enter key: hello world
安全を気にするOpenBSDには、enigmaもbdesも入っていない。これは、親分からの使うな危険と いう暗黙のメッセージです。まあ、SSL系を素直に使っておk。
$ openssl version LibreSSL 2.6.3 $ echo hello world | openssl aes-256-cbc -e > xx enter aes-256-cbc encryption password: Verifying - enter aes-256-cbc encryption password: $ hexdump -C xx 00000000 53 61 6c 74 65 64 5f 5f 34 22 83 31 fe ee 2c 5d |Salted__4".1..,]| 00000010 9f 09 6b 96 9a 9e 34 90 d8 dc 8b d5 e8 54 bd 98 |..k...4......T..| $ cat xx | openssl aes-256-cbc -d enter aes-256-cbc decryption password: hello world
mongo
余りbinaryばかりやっていても秋るので、先週始めたmongoに戻ります。これってリナの特許じゃ無いよね。OpenBSDにも有るか確認。有れば、天下公認です。
$ grep mongo JAIST mongodb-3.2.13p0.tgz 42M py-mongo-3.4.0.tgz 463K py3-mongo-3.4.0.tgz 457K
操作は、pythonからどうぞって、現代風ですな。
ここに、特徴とオブジェクトのidの付け方の説明が出てた。 MongoDBをpythonから利用する
mongoDB on Haskell
へそ曲がりなオイラーは、pythonとかを敬遠して、Haskellです。集積地で探したら、 The mongoDB packageが、一番人気だったので、そのHomeへ行ってみた。 MongoDB driver for Haskell
ちゃんとしたドキュメントが有るので、迷わないだろう。ドッカーで使うってのがお勧めみたいだ。まあいいや、debian上でやってみる。
まずは、Quick exampeleが動くかだな。
deb9:tmp$ stack new db simple deb9:tmp$ cd db/ deb9:db$ ls db.cabal LICENSE README.md Setup.hs src stack.yaml deb9:db$ emacs src/Main.hs
ダミーのプロジェクトを作って、Main.hsを例に置き換える。 そして、ビルドで、必要品を教えてもらう。
deb9:db$ stack build db-0.1.0.0: configure (exe) Configuring db-0.1.0.0... db-0.1.0.0: build (exe) Preprocessing executable 'db' for db-0.1.0.0... [1 of 1] Compiling Main ( src/Main.hs, .stack-work/dist/x86_64-linux -nopie/Cabal-1.24.2.0/build/db/db-tmp/Main.o ) /tmp/db/src/Main.hs:6:1: error: Failed to load interface for ‘Database.MongoDB’ Use -v to see a list of the files searched for. /tmp/db/src/Main.hs:10:1: error: Failed to load interface for ‘Control.Monad.Trans’ Perhaps you meant Control.Monad.Fail (from base-4.9.1.0) Control.Monad.Trans.RWS (needs flag -package-key transformers-0.5.2.0) Control.Monad.Fix (from base-4.9.1.0) Use -v to see a list of the files searched for.
適当にdb.cabalに追加して、再度ビルド
mongoDB-2.3.0: copy/register db-0.1.0.0: configure (exe) Configuring db-0.1.0.0... db-0.1.0.0: build (exe) Preprocessing executable 'db' for db-0.1.0.0... [1 of 1] Compiling Main ( src/Main.hs, .stack-work/dist/x86_64-linux -nopie/Cabal-1.24.2.0/build/db/db-tmp/Main.o ) /tmp/db/src/Main.hs:10:1: error: Failed to load interface for ‘Control.Monad.Trans’ It is a member of the hidden package ‘mtl-2.2.1’. Perhaps you need to add ‘mtl’ to the build-depends in your .cabal file. Use -v to see a list of the files searched for. Completed 57 action(s).
まだエラーなんで、最終的にdb.cabalを下記のようにした。
build-depends: base >= 4.7 && < 5 , transformers , mongoDB , mtl
これで、ビルドが成功した。mongoDBって、セキュリティがらみで、随分と色々なモジュールを コンパイルしてたぞ。オイラー的には、もっとカジュアルな奴でいいんだけどね。
deb9:db$ stack exec db All Teams [ name: "Red Sox", home: [ city: "Boston", state: "MA"], league: "American"] [ name: "Yankees", home: [ city: "New York", state: "NY"], league: "American"] [ name: "Mets", home: [ city: "New York", state: "NY"], league: "National"] [ name: "Phillies", home: [ city: "Philadelphia", state: "PA"], league: "National"] National League Teams [ name: "Mets", home: [ city: "New York", state: "NY"], league: "National"] [ name: "Phillies", home: [ city: "Philadelphia", state: "PA"], league: "National"] New York Teams [ name: "Yankees", league: "American"] [ name: "Mets", league: "National"] ()
赤い靴下ってボストンがホームグラウンドなんだ。オイラーは、そんなよその国のことはおろか、日本のチームもよく知らん。
こういう例をたたき台にして、誰かさんは馬のDBを作るだろう。なんたって最初から、がちがちにテーブル設計する必要無し。
基礎データをJRAから買ってきて、取り合えずのDBを作る。後は、自分のノウハウをDBに追加してく。
昔の会社の同僚は、お舟に凝ってて、自前のDB(もどき)を、EXCELで作ってた事を知ってるぞ。舟で当てて、御殿に住んでるのかなあ?
db shell
上で作ったデータが残っているはずなので、お試しをしてみる。
deb9:tmp$ mongo MongoDB shell version: 3.2.11 connecting to: test > show dbs baseball 0.000GB local 0.000GB > use baseball switched to db baseball > db.stats() { "db" : "baseball", "collections" : 1, "objects" : 4, "avgObjSize" : 105, "dataSize" : 420, "storageSize" : 32768, "numExtents" : 0, "indexes" : 1, "indexSize" : 32768, "ok" : 1 } > show collections team
登録されてるDBを確認。DBを切り替え。状態をチェック。コレクションってのがRDBMSで言う、 テーブルの事らしい。
> db.team.find() { "_id" : ObjectId("59f6c9ff6e955226ab000000"), "name" : "Yankees", "home" : { "city" : "New York", "state" : "NY" }, "league" : "American" } { "_id" : ObjectId("59f6c9ff6e955226ab000001"), "name" : "Mets", "home" : { "city" : "New York", "state" : "NY" }, "league" : "National" } { "_id" : ObjectId("59f6c9ff6e955226ab000002"), "name" : "Phillies", "home" : { "city" : "Philadelphia", "state" : "PA" }, "league" : "National" } { "_id" : ObjectId("59f6c9ff6e955226ab000003"), "name" : "Red Sox", "home" : { "city" : "Boston", "state" : "MA" }, "league" : "American" } > db.team.find( {}, {_id:0} ) { "name" : "Yankees", "home" : { "city" : "New York", "state" : "NY" }, "league" : "American" } { "name" : "Mets", "home" : { "city" : "New York", "state" : "NY" }, "league" : "National" } { "name" : "Phillies", "home" : { "city" : "Philadelphia", "state" : "PA" }, "league" : "National" } { "name" : "Red Sox", "home" : { "city" : "Boston", "state" : "MA" }, "league" : "American" }
全件確認。idが邪魔なので、除いて表示。0とか1は、false、trueの意味とは、変な所で、JavaScriptの素性をさらけ出すのね。
> db.team.find( {}, {_id:0, home:1} ) { "home" : { "city" : "New York", "state" : "NY" } } { "home" : { "city" : "New York", "state" : "NY" } } { "home" : { "city" : "Philadelphia", "state" : "PA" } } { "home" : { "city" : "Boston", "state" : "MA" } } > db.team.find( {}, {_id:0, home:{city:'Boston'}} ) Error: error: { "waitedMS" : NumberLong(0), "ok" : 0, "errmsg" : "Unsupported projection option: home: { city: \"Boston\" }", "code" : 2 }
homeフィールドだけ検索。そして絞り込もうとすると、あえなくエラーを喰らった。何か逃げの手がありそうだな。Haskell版では実行出来ていたからね。
> db.team.find( {}, {_id:0, 'home.state':'NY'} ) { "home" : { "state" : "NY" } } { "home" : { "state" : "NY" } } { "home" : { "state" : "PA" } } { "home" : { "state" : "MA" } } > db.team.find( {}, {_id:0, {'home.state':'NY'}} ) 2017-11-11T17:05:35.403+0900 E QUERY [thread1] SyntaxError: invalid property id @(shell):1:26
なかなか、思い通りに行かないなあ。
> db.team.find( {'home.city': 'Boston'} ) { "_id" : ObjectId("59f6c9ff6e955226ab000003"), "name" : "Red Sox", "home" : { "city" : "Boston", "state" : "MA" }, "league" : "American" } > db.team.find( {'home.city': 'Boston'}, {_id:0, home:1} ) { "home" : { "city" : "Boston", "state" : "MA" } }
で、ちょっと想像を働かせたら、思い通りになった。
mongodb web console
こちらあたりが、mongoを使う上でのまとめになるかな。まあ、shellから、db.help()とか やってもいいんだけど、cookbook的によいかも。
それで、Javascriptがあちこちに顔を出す。大体、dbへの格納もJSONをバイナリーにしたもの だしね。Javascriptなら、ブラウザーとつるんで、悪さをするのは、得意中の得意のはず。 そう思って資料を漁ると、web consoleですってさ。
Debianのデフォでは、Webは危険(あくまで、個人的感想です)って事で、有効になっていない。設定ファイルを下記のように変更すると、ブラウザーからアクセス出来るようになる。
deb9:~$ sudo vi /etc/mongodb.conf httpinterface=true rest=true deb9:~$ firefox http://localhost:28017
ああ、設定変更したら、サーバーを再起動するのよ。そうすれば、ブラウザーからアクセス出来るようになる。
後は、下記の例を参照。
説明によると、地理的情報を扱うのも得意だそうなので、行動記録を残すのも簡単だろう。
consoleから、各種の状態が取れる。例えば、buildinfo
{ "version" : "3.2.11", "gitVersion" : "009580ad490190ba33d1c6253ebd8d91808923e4", "modules" : [], "allocator" : "tcmalloc", "javascriptEngine" : "mozjs", "sysInfo" : "deprecated", "versionArray" : [ 3, 2, 11, 0 ], "openssl" : { "running" : "OpenSSL 1.0.2l 25 May 2017", "compiled" : "OpenSSL 1.0.2j 26 Sep 2016" }, "buildEnvironment" : { "distmod" : "", "distarch" : "x86_64", "cc" : "cc: cc (Debian 6.2.1-6) 6.2.1 20161212", "ccflags" : "-fno-omit-frame-pointer -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Wno-nonnull-compare -Wno-overflow -Winvalid-pch -Werror -O2 -Wno-unused-local-typedefs -Wno-unused-function -Wno-deprecated-declarations -Wno-unused-const-variable -Wno-unused-but-set-variable -Wno-missing-braces -fno-builtin-memcmp", "cxx" : "g++: g++ (Debian 6.2.1-6) 6.2.1 20161212", "cxxflags" : "-g -O2 -fdebug-prefix-map=/build/mongodb-NMhPVq/mongodb-3.2.11=. -fstack-protector-strong -Wformat -Werror=format-security -Wnon-virtual-dtor -Woverloaded-virtual -Wno-maybe-uninitialized -std=c++11", "linkflags" : "-Wl,-z,relro -fPIC -pthread -Wl,-z,now -rdynamic -fuse-ld=gold -Wl,-z,noexecstack -Wl,--warn-execstack", "target_arch" : "x86_64", "target_os" : "linux" }, "bits" : 64, "debug" : false, "maxBsonObjectSize" : 16777216, "storageEngines" : [ "devnull", "ephemeralForTest", "mmapv1", "wiredTiger" ], "ok" : 1 }
JSON様々だな。