【06】見知らぬ人ともうまくやるには

小飼 弾

 バグには 2 種類しかありません。1 つは、出来るはずのことが出来ないこと。もう 1 つは、出来てはならないことが出来てしまうこと。そんなこと言われるまでもない? しかしプログラマという人種は前者には十分な注意を払っても、後者には前者ほど注意を払わない生き物のように思われます。

 SQL インジェクションに CSRF……いわゆる脆弱性というのはすべて後者に属するバグで、しかも必ずサービスが始まってから顕在化します。こうした脆弱性が後を絶たないのは、プログラマが出来なかったことを出来るようにすることには一生懸命でも、そこで力つきてしまって、出来てはならないことが本当に出来ないかをチェックをするだけの余裕がないことの現れかもしれません。

 これを防ぐには一体どうしたらよいのでしょうか?

 私は LLEval という Web サービスを提供しています。任意のコードを実行するという Web サービスで、類似サービスには codepad などがあります。こうしたサービスはどうやって

#!/usr/bin/perl
system qw{rm -rf /}

のような「出来てはならない」ことを出来ないようにしているのでしょう?

 結論から言うと、システムコールを監視し、実行されてはならないシステムコールを受け取ったら即座に実行を停止するという方法を採っています。例えば上記のコードであれば unlink システムコールが必ず発行されますが、これを許可しなければよいわけです。

 しかしこれでうまく行くのも、アプリケーションプログラムが「何かするため」には、システムコールを経由しなければならないという OS の仕組みがあってこそです。古き佳き MS-DOS の頃ならさておき、現代的な OS はすべてそうなっています。I/O などの計算機資源にアクセスするには、システムコールという API を介するしかないのです。

 ここに大いなるヒントがあります。出来てはならぬことを出来なくするには、「出来てはならぬことを禁じる」のではなく、はじめから「出来ていいことだけを出来るようにする」と考えるのです。

見知らぬ人とうまくやる一番のコツは、見知らぬことをしないこと。

 とはいえ、「出来てはいいこと」を定義するのは案外難しいものです。rm -rf / だって、スーパーユーザーであれば出来なければならないことだと「定義されて」います。スーパーユーザーに出来ないことはあってはならないというのが Unix の流儀ですから。だからこそ wheel 登録されているユーザーも、必要な時のみ su ないし sudo でスーパーユーザー権限を取得するというのが作法にもなっているのですが。

 誰に、いつ、なにを許可するのかというのは、プログラマに限らず人間社会の永遠の課題であり続けるでしょう。それでも「出来ていいことだけを出来るようにする」をデフォルトにするだけで、この課題は格段に楽になるはずです。