JSONの見せかけの単純さ
7分40秒 | JSON
基本情報技術者試験の頻出テーマを解説した動画コンテンツです。
トランスクリプト(字幕テキスト)
いや、僕たちが毎日当たり前に使っているツールって、一見すごくシンプルに見えて、実はその裏に、ものすごく奥深い複雑さが隠されていることってありますよね。 今回は、ウェブの世界をまさに裏で支えているJSON。このJSONが持つディセプティブ・シンプリシティ、つまり見せかけの単純さっていうところにグッと迫っていきたいと思います。 さて、物語はまずここから始めましょう。現代のウェブを成り立たせている、まあ言ってみればヒーローですよね。ウェブのユニバーサル翻訳機の話です。 ちょっと想像してみてください。あなたがReactでめちゃくちゃクールなアプリを作ったとします。 それから、Google Apps Script、いわゆるGASで、すごく賢い自動化の仕組みも作った。さあこの2つ、どうやってお互いに会話させますか? たとえどっちもJavaScriptがベースになっていたとしても、なんていうか方言みたいなものがあって、そのままじゃお互いの言っていることを100%は理解できないんですよ。 インターネットっていう広い海を渡るには、データをちゃんとした標準的な形、つまり共通言語にパッケージし直してあげる必要があるんです。 そこで登場するのがJSON、JavaScript Object Notationです。これがまさに普遍的なデータの梱包ロールなんですね。 ソースにあったデータのフリーズドライっていう例えが、もう本当に秀逸で、送る時はシンプルなテキストにギュッと固めて、 目的地に着いたらお湯をかけるみたいに完璧に元の形に戻す。この仕組みがウェブ上のあらゆるコミュニケーションの土台になっているわけです。 じゃあこのデータの梱包、つまりフリーズドライして、そして元に戻すっていうプロセスが具体的にどんな仕組みで動いているのか、ちょっと中身を覗いてみましょうか。 はい、この表がJSONの核心をすごくわかりやすく示してくれてます。左側がプログラムのメモリの中で生きている、いわばナマのデータであるJavaScriptオブジェクト。 で、右側がインターネットを旅するために荷造りされたただのテキスト、JSON文字列です。 注目してほしいのはJSONのルールの厳格さ。例えばキーを見てください。JavaScriptだと引用符がなくてもいい場合があるんですけど、JSONでは絶対にダブルクォーテーションで囲まないといけない。 値として使えるデータ型も、関数みたいな複雑なものはダメで、ごくごく基本的なものだけに限られてるんです。なんでこんなに厳しくするの?って思いますよね。 でも、この厳しさがあるからこそ、データを受け取った相手がPythonだろうがGoだろうが、どんな言語でも「あ、これはこういうデータだな」って正確にたった一つの意味に解釈できるわけです。 この厳格さこそが特定の言語に縛られない真のユニバーサル翻訳機としての信頼の源泉なんですね。 このデータの交換は、まあ一種の儀式みたいなもので、2つのステップで行われます。まず送り手、例えばReact側では、JSON.stringifyっていう呪文を唱えて、 オブジェクトをテキスト文字列に変換します。これがフリーズドライの工程ですね。 そして受け手側、GASではJSON.parseを使って、送られてきた文字列をまたプログラムで使えるオブジェクトに解析する。つまり、元に戻すわけです。 なるほど、と。すごく完璧なシステムに見えますよね。でも、もし、その翻訳が必ずしも完璧じゃなかったとしたらどうでしょう。 さあ、ここから物語は一気にサスペンスの領域に入っていきます。その翻訳に欠陥があったとしたら、一体何が起きてしまうんでしょうか? そしてこれが衝撃的な事実です。セキュリティの調査会社ビショップ・フォックスはこう言ってるんですね。 同じJSONドキュメントでも、マイクロサービスごとに違う値として解釈されちゃって、それがいろんなセキュリティリスクにつながる可能性があると。 あれだけ厳格だと思っていたJSON의 단순さは、実は見せかけだったのかもしれないです。 問題の根本はどこにあったのかというと、なんと公式の仕様書、RFC 8259の驚くべき柔軟性にあったんです。 例えば同じキーがデータの中にダブって入っていた場合、どういう動きをすべきか。 仕様書にはなんと「予測不可能」って書いてあるんですよ。これじゃパーサー、つまりデータを解析するプログラムによって、 最初の値を採用したり最後の値を採用したり、あるいはもうエラーにしちゃったりと、解釈がバラバラになっちゃうのも、まあ無理はないですよね。 では、このちょっと抽象的な問題を、現実の世界で起こりうる「重複キー強盗」というちょっとゾッとするシナリオで、具体的に見ていきましょう。 とあるEコマースサイトを想像してください。このサイトでは、2つのマイクロサービスが動いています。 1つはお客さんのカートの中身が正しいかチェックするPythonのサービス。そしてもう1つは実際の支払いを処理するGo言語のサービスです。 ここに悪意のある攻撃者がQTY、つまり数量のキーをわざと2つ入れた特殊なJSONリクエストを送り込んできます。 最初の数量は-1、そして2つ目の数量は1。これが巧妙なデジタルの罠の始まりです。 そしてこのスライドがその恐ろしい結果をはっきりと示しています。解釈に致命的なズレが生まれる瞬間です。 パーサーA、つまりPythonの検証サービスは重複したキーの最後の値、QTY 1を見ます。「うん、数量1個なら問題ないな」とカートを有効だと判断する。 ところが、パーサーB、Goの決済サービスは最初の値、QTY -1を見てしまうんです。「え、支払い額がマイナス?」っていうことになっちゃうんですよ。 結果何が起こるか。検証サービスはカートを承認する。でも、決済サービスは不正な合計金額で処理を進めてしまう。 この例だと、お客さんは700ドル分の商品を手に入れたのに、請求されたのはたったの300ドル。 差額の400ドルは完全に企業の損失です。怖いですよね。 じゃあ、こんなデジタル強盗みたいなことを防いで、全ての翻訳機が同じ台本を同じように読んでくれるようにするには、 一体どうすればいいんでしょうか。その答えは、規律ある解読です。 専門家の調査に基づいた、開発者が取るべき明確な対策がこれです。まず第一に、キーが重複していたら「どっちだろう」なんて推測しないで問答無用でエラーにする。 第二に、無効なUnicode文字があったら、データを勝手に切り捨てたりせず、ちゃんとプレースホルダーに置き換える。 第三に、RFC 8259のように、決めた仕様にはとにかく厳密に従う。そして最後に、とてつもなく大きい数字みたいに、システムで正確に表現できないものはエラーにする。 要するに、曖昧さをシステムから徹底的に排除するということなんです。 というわけで、ウェブの共通言語であるJSONの、あのシンプルさの裏側には、解釈のズレっていう危険な落とし穴が巡っていたわけです。 最後に、この問いを皆さんに投げかけて終わりたいと思います。シンプルなテキストの上に成り立っているこのデジタルの世界で、僕たちの知らないところで、ほかにどんな重要な翻訳が今まさに崩壊しつつあるんでしょうか。
このコンテンツは Web society で視聴・学習できます。