ikepyonのだめ人間日記

セキュリティに関することを書いていく予定。

コードによる脆弱性が発生する仕組み

コードによる脆弱性が発生する原理は、XSSにしろ、SQLインジェクションにしろおんなじ何だが、理解できてない人には理解できないみたいなようなのでつれづれに書いてみる。
どんなアプリケーションも以下のような構造をしている。

入力データ→モジュールA→データ1→モジュールB→データ2→モジュールC→データ3→・・・・・→モジュールX→出力データ

要するに、どこかしらから入力データを受け取ったモジュールが、そのデータをごちゃごちゃ処理を行い、その結果をデータとして別のモジュールに渡す。そしてデータを受け取ったモジュールが今度はそのデータを元に処理を行って、また新しいデータを出力する。これを延々伝言ゲームのように繰り返すわけだ。
まあ、大きく考えると一つのモジュールはブラウザ、RDBMS、Webアプリケーションといったものになるし、粒度を小さくして関数やメソッド単位にして考えてもいい。どっちにしろ、あらゆるアプリケーションは複数のモジュールを組み合わせて作られている。

さて、伝言ゲームでは、受け取った情報を隣の人に伝えてるだけなのに、最初の人が受け取った内容と、最後の人が受け取った内容がまったく異なることが多々ある。これは、各人が受け取った情報の伝達にミスがあるとか、勝手に解釈するからというのがあるために発生するわけである。コードによる脆弱性もまったく同じようなことが起こっているために起こる。

つまり、モジュール内では問題なく解釈し処理を行っているのだけれど、他のモジュールに渡すデータが受け取るモジュールからするとあいまい(正確に言うと違うのだけれど)というか別の意味に解釈できるようなデータを渡してしまう。この結果、開発者が意図しない処理を行ってしまう。これが、脆弱性として表面化するのである。受け取るデータが明確におかしなもの(伝言ゲームの例で言えば、出題内容が日本語とされているにもかかわらず日本語の文法に沿ってないものとか)であれば、受け取る側で「コレはおかしい!」と判断して、エラーなり、なんなり処理ができるんだけれども、たまたまデータはおかしくないけれど、別の意味に取れるようなものであれば、別の意味として処理を行ってしまう。

日本語で言えば、例えば次のような文章があったとする。

ここではきものをぬいでください

コレを書いた人(開発者)は「ここで履物を脱いでください」という意味でこの文章を書いたにもかかわらず、受け取った人(RDBMSとか他のモジュール)は「ここでは着物を脱いでください」ととって服を脱ぎだすということが起こっているかもしれない。
元の文は「は」を「HA」と読むか、「WA」と読むかで大きく意味が違ってくる。
日本語では「は」という文字は特別な意味を持っており、「WA」と読むことで「助詞?」になったり、「HA」と読むことでただの「は」という文字になったりする。先の文を一意の意味を持つようにするためには以下のようにすればいい。

ここで、はきものをぬいでください

こうすることで「は」は「HA」としか取れなくなる。もちろん「はきもの」を「くつ」に変えてしまって、「ここでくつをぬいでください」としても日本語としてはほぼ同じ意味を持つので問題ないと言うかもしれない。しかし、文章が改変されてしまい、もとの文章とは違うものになってしまっている。「くつ」をぬげとあるが、では「げた」は脱がなくてもいいのか?とか「さんだる」は?という具合に厳密に考えると不具合が起こる。もっとも、人間はいい加減なので「くつ」=「はきもの」と解釈するので現実社会では問題は起こらないだろう。


これと同じこと(「は」を「WA」でなく「HA」と読んでしまうこと)がコードの脆弱性でも起こっている。例えばSQLインジェクションであれば「'」は文字列リテラルの区切りを意味するので「Tully's」といった文字列リテラルをそのまま検索しようとすると「'」が文字列リテラルの終了とみなされて、SQL文法エラーが発生してしまう。
これを防ぐには「は」を「HA」と確実に読ませるように「、は」としたように、「'」を文字列リテラルの区切りをいう意味を持たないただの文字データ「'」として表す必要がある。そのためには「'」を「''」にするという処理を行う。こういった特別な意味を持つデータを特別な意味を持たないデータにする処理をエスケープ処理と呼ぶ。この処理を行うことで、RDBMSは、開発者の意図どおりの意味を解釈することができる。

こう理解するとコードの脆弱性対策って当たり前のことと思えないかなぁ?