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

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

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

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

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

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

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

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

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

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

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

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