ウェブアプリケーションセキュリティにおける、ホワイトリスト、ブラックリストについての意見です。

用語の定義

まず、用語ですが、ホワイトリストについては、「許可された入力値をリストとして列挙したもの」でよいと思います。ブラックリストは「拒否する入力値をリストとして列挙したもの」でよいと思います。

実装という観点から

セキュリティの専門家だと、自分ではプログラムを書かない(プロダクト作成はしない)、という方もいるようですが、私は実際にプログラムを書くことが多いので、実装する立場で考えます。

ホワイトリストでも、ブラックリストでも実装できる場合を考える

「ブラックリストでないと実装できない」ケースについては、ブラックリストで実装しなければなりませんから、ここは議論しないことにします。(たとえば「原則誰でも登録できるサービスだが、スパム行為を行ったアカウントはブロックする」ようなケース)

そして、ホワイトリストでも、ブラックリストでも実装できる場合とは、具体的には下記のようなケースです。

  • 0と1を除く数字1文字
  • プライベートアドレスを除いたIPアドレス(IPv4)
  • 制御記号を除いた文字で構成される文字列で、長さ10文字

「0と1を除く数字1文字」のケースで、実装してみます。入力はstring型を前提にします。

ホワイトリストでの実装

「2, 3, 4, 5, 6, 7, 8, 9 のいずれか」というように、ホワイトリストで列挙することができます。検証結果は$validated変数に格納するようにしています。

$validated = false;
$valid_array = array('2', '3', '4', '5', '6', '7', '8', '9');
if (in_array($input, $valid_array, true) {
    $validated = true;
}

のようなコードになります。

ブラックリストでの実装

$validated = true;
$invalid_array = array('0', '1');
if (in_array($input, $invalid_array, true) {
    $validated = false;
}

のようなコードになります。

ブラックリストでの実装を良く見ると

ブラックリストの実装を見直してみます。たしかに、「0」と「1」は不許可にしていますね。また、「2」から「9」までを許可していますね。しかし、この実装では不十分です。なぜなら、「数字1文字」という条件が抜けているからです。この実装だと、「a」や「23」なども許可してしまいます。

「a」や「23」を正しく弾くには、

$validated = false;
if (ctype_digit($input) && strlen($input) === 1) {
    $validated = true;
}
$invalid_array = array('0', '1');
if (in_array($input, $invalid_array, true) {
    $validated = false;
}

のようにしなければいけません。

図で確認

ホワイトリストでコードを書いた場合、ブラックリストでコードを書いた場合、をそれぞれ、ベン図で確認してみましょう。

まずはブラックリストからです。

ブラックリスト

上の図のようになります。ブラックリストで実装すると、紫の丸部分だけでなく、青の丸部分も実装しなければなりません。

一方、ホワイトリストでコードを書いた場合は、下の図のようになります。

ホワイトリストの場合は、黒の丸部分を実装すればOKです。

ブラックリストは、ホワイトリスト+ブラックリスト

ブラックリストでは、紫の丸部分と青の丸部分を実装しました。紫の丸部分は一般にブラックリストと呼ばれている部分です。青の丸部分は、「数字1文字」で、いわゆるホワイトリストです。

つまり、ブラックリストでの検証は、より広い範囲でのホワイトリスト(許可される範囲)を前提とし、その上でブラックリスト(除外される範囲)を設定しているのではないか、ということです。

実際、「プライベートアドレスを除いたIPアドレス(IPv4)は、

  • IPアドレス(許可される範囲)
  • プライベートアドレス(除外される範囲)

に分けられるでしょう。

「制御記号を除いた文字で構成される文字列で、長さ10文字」も、

  • 1バイトの文字が10個並ぶ(許可される範囲)
  • 制御記号(除外される範囲)

に分けられるのではないかと思います。「マルチバイト文字でも制御記号でないものは許可対象」という意見もあると思います。その場合は、許可される範囲が上よりももっと広い範囲になると思われます。

実際、「許可される範囲=すべての入力」というケースは別にすると、ブラックリストであっても、許可される範囲の前提があるのではないでしょうか。

ブラックリストは実装量が多い

ブラックリストで実装する場合に、「ホワイトリスト(許可される範囲)」と「ブラックリスト(除外される範囲)」の実装が必要、ということであれば、ホワイトリストよりも実装量が多くなるでしょう。

ブラックリストで実装量が多いのであれば、間違いが起こりやすいと思われます。この点が私が「ホワイトリストよりブラックリストが良い」と考える理由です。

まとめ

長い記事になりましたが、まとめると

  • ホワイトリストでもブラックリストでも実装が可能なケースがある
  • ブラックリストでは、より広い範囲でのホワイトリスト(許可される範囲)を前提としている
  • ブラックリストのほうが実装量が多く、間違いが起こりやすい

となります。

記事公開日: 2016年03月29日
#

PHP5.6系は、2016年末でアクティブサポートが終了、2018年末... 詳細はこちら

Nagoya.PHP 第12回に参加しました。会場はカルテットコミュニ... 詳細はこちら

「営業・運用を支える 気付ける 管理画面 」に興味があって参... 詳細はこちら