MessagePack と壊れたデータ

MessagePack のエンコード方式をよく見ていると気づくことだが、MessagePack のフォーマットにおいてはたいていのバリエーションのバイト列が MessagePack のバイナリフォーマットとして合法になる仕様である。したがって、うっかり変な出力をしてしまうプログラム*1が居た場合、その出力をデコードした結果がとんでもないデータになる可能性がある。


たとえばとてつもなく長いrawや要素数の多いコンテナ*2がデコード結果になったり、あるいはそこまでひどくなくとも、本来オブジェクトが来てほしい場所に整数が出てくる、といった事故は簡単に起こりえる。

MessagePack を「高速な JSON*3として使う場合、JSON といった類のフォーマットでは意図しない壊れたマークアップがほぼ確実にデコード時に検出されていたのに対して、MessagePack ではフォーマットに無駄がないが故にロバスト性もないということには注意が必要である。

具体的には、MessagePack でデコードした結果のデータの構造は、今まで JSON などを用いていた場合は(パースエラーなどになってくれていたので)心配くて済んでいた局面においても、デコード結果が正しくないことがあると考えた上でチェックする*4などといった注意が必要になりそうである。

lたとえば、逐次的に JSON をはき出す web API (PHP で実装)があったとすると、web API の実装が発狂した*5場合、JSON の途中にいきなり例外メッセージが出てくるので、クライアント側は JSON パースエラーによる web API 呼び出しの例外によって、発狂したサーバーのレスポンスをまともに取り扱ってしまうことはなかった。しかし、これをそのまま何も考えずに MessagePack に置き換えると、おそらくデバッグの面倒な*6例外になるであろう。

もちろん、今まで JSON を用いていたときでも、JSON として合法ながらも意図しない構造のデータが紛れ込むことは原理的にはあり得たのだが、MessagePack ではそれが起こり得るシチュエーションが増えるため、よりきちんとしたチェックを MessagePack を使う側が行う必要があるだろう、ということである。

余談だが、Protocol Buffer が IDL を書くようになっているのも、こういったデータの整合性の問題も背景にあってのことなのかもしれない、と思わされる。実際、MessagePack を使う場合、より上位のアプリケーション層で何らかの IDL 的なものとのマッチングを行うようにすることは、上記のチェックを実現するための手段として悪くないように思える。

さらに言うと、Protocol Buffer はデータをエンコードするだけでなく、Verify を行うということも含んでいるのに対し、MessagePack はエンコードだけに徹し、その正当性の検証等々はより上位で行うべき、という思想なのであろうし、これが id:viver が Protocol Buffer と MessagePack が根本的に異なると主張してる所以なのであろう。個人的には、直交性があるものは好きなので、スイート化したメカニズムよりもこういうものを応援したいものである。といいつつ互換実装を作るとへぼいものが出来てしまいそうだけどwww

*1:とくに PHP などの場合、言語使用などの関係上意図しないバイトが標準出力されてしまうという事故は非常によくあることである。

*2:これによるメモリ枯渇や DOS を防ぐための機構はデコーダー側にも必要そうだ。

*3:id:viver 曰く

*4:か、自然とチェックがかかる使い方にする

*5:開発過程ではよくあること

*6:web API リクエストの取得とは関係ないところでバグが表面化するので、わかりにくい