今日、ダイナミックなコンテンツを提供するウェブアプリケーションに
おいてはデータベースは欠く事のできなコンポーネントとなっています。
そういったデータベースには重要な、そして秘密にすべき情報が格納
されることになるので、それらをいかにして保護するかについて十分に 考慮する必要があります。
情報を取り出したり格納するためにはデータベースに接続する必要があります。
そして適切なクエリを送信し、結果を受け取り、切断します。クエリに 使用される言語はStructured Query
Language (SQL)が一般的です。アタッカー がどのように
SQLに
干渉する
かについて参照してください。
皆さんがお気づきの様に、PHPそれ自体は貴方のデータベースを保護することは
ありません。以下のセクションはPHPスクリプトからどのようにデータベースに
アクセスし操作すればいいのか、とういことに関する非常に基本的な導入です。
このシンプルなルールを覚えて置いてください:それは「多重防衛」です。
より多くの箇所で、より多くの保護を行うことにより、アタッカーが攻撃に
成功して機密情報が漏洩する可能性は減っていきます。データベースと
アプリケーションを正しくデザインすることで貴方の心配を取り除くことが できます。
他人が用意した既存のものを使用するのでない限り、最初に行うのはデータベースの作成です。
データベースが作成されると、そのデータベースのオーナーは作成コマンドを
実行したユーザになります。通常、オーナー(とスーパーユーザー)のみが
そのデータベースに対して操作を行うことが出来ます。他のユーザがデータベースを
使用するには適切な権利が与えられている必要があります。
アプリケーションはデータベースにオーナー、もしくはスーパーユーザーとして
接続しては絶対にいけません。なぜならこれらのユーザは
例えばスキーマの変更(テーブルの削除等)や全コンテンツの削除、といった
あらゆるクエリーを実行することが出来るからです。
貴方が作成するアプリケーションがデータベースに対して行う操作の各方面ごとに、
捜査対象となるオブジェクトに対して、出来る限り少ない権限を持った複数の
ユーザを作成した方が良いでしょう。ユーザに対しては、最低限必要な権限のみを
与え、関係の無いデータへのアクセスを許可しないようにします。これは、
万が一侵入者がそのユーザの権限を以ってデータベースにアクセスした際に、
アプリケーションと関係の無いデータにまでアクセスされることを防ぐためです。
全てのビジネスロジックをウェブアプリケーション(つまり貴方のスクリプト)
で実装することは推奨されません。代わりに、ビュー、トリガー、ルールといった
データベースの機能を活用した方が良いでしょう。システムが更新され、
新しい機能がデータベースへのアクセスすることになった場合、個々のデータベース
クライアントごとに再度同様のロジックを実装しなければならなくなります。
さらに、トリガーは透過的に、そして自動的にフィールドを扱うことが出来るので、
デバッグ時や、トランザクションのロールバック時に役立ちます。
更なるセキュリティのために、クライアント/サーバ間の通信においてSSLを用いた
暗号化を行った方が良いでしょう。もしくはsshを使用することも出来ます。
どちらかの手段を講じた後、トラフィックをモニタリングしてみれば
ここから何らかの情報を得ることが困難だという事が分かると思います。
SSL/SSHによってクライアント/サーバ間で通信されるデータは保護されますが、
データベースに保存されたデータは保護されません。SSLはあくまで通信上の プロトコルなのです。
一旦アタッカーがデータベースへ(ウェブサーバを通さずに)アクセスできてしまうと、
そこに格納されているデータ自体が暗号化されていない限り、自由に閲覧され、
使用されてしまいます。データを暗号化することによって、この脅威を減らすことが
できますが、この機能をサポートしているデータベースは僅かです。
この問題への最も簡単な対応策は、まず自分専用の暗号化パッケージを作成し、
それをあなたのPHPスクリプトから使用することです。PHPの
Mcrypt
,
Mhash
といった幾つかの拡張モジュールは、様々な暗号化アルゴリズムをサポート
しているので役に立つでしょう。データ格納時に暗号化を行い、取得時に
復号化します。この方法についてはリファレンスを参照してください。
もし完全にデータを隠したい場合や、元のデータ自体は必要ない場合(つまり
表示されない場合)は、ハッシュも考慮に入れたほうが良いでしょう。
ハッシュの良く知られた使用方法は、パスワードをそのまま格納せずに、 そのMD5ハッシュ値を格納する方法です。
crypt()
や
md5()
も参照してください。
多くの開発者はSQLクエリがどのように改竄されるかということを余り
気にかけておらず、またSQLクエリは信用できるものと考えているようです。
実際にはSQLクエリはアクセス制限を回避することが可能で、従って
通常の認証や権限のチェックを無視することができます。時には、
OSレベルのコマンドを実行できてしまうこともあります。
Direct SQL Command
Injection(SQLコマンドの直接実行)という手法は、
攻撃者がSQLコマンドを生成もしくは既存のコマンドを変更することで
隠蔽すべきデータを公開したり、重要なデータを書き換えたり、データベース
ホストで危険なシステムレベルのコマンドを実行したりするものの事です。
この手法は、ユーザからの入力をスタティックなパラメータと組み合わせて
SQLクエリを生成するアプリケーションにおいて使用されます。以下の例は 不幸なことに実際の事例に基づいたものです。
入力のチェックを怠っており、スーパーユーザもしくはデータベース作成権限を
持つユーザ以外のユーザでデータベースに接続
していない
ために、攻撃者はデータベースにスーパーユーザを作成することが出来ます。
// PostgreSQLの場合 0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; -- // MySQLの場合 0; UPDATE user SET Password=PASSWORD('crack') WHERE user='root'; FLUSH PRIVILEGES; |
注意 SQLパーサにクエリの残りの部分を無視させるために開発者によく使わ れる技法として、SQLのコメント符合である -- があ ります。
パスワードを取得する恐るべき手段に、サイトの検索結果のページを欺く
というものがあります。攻撃する者が必要とするものは、投稿された変数
の中でSQL命令で使用される際に正しく扱われていないものがあるかどう かを確かめるだけです。これらのフィルタは、通常、
SELECT
文の
WHERE, ORDER BY, LIMIT
及び
OFFSET
句をカスタマイズするた めに前に置かれる形で設定されます。使用するデータベースが
UNION
構造をサポートしている場合、 攻撃者は元のクエリに任意のテーブルからパスワードのリストを取得する
クエリを追加しようとするかもしれません。 暗号化されたパスワードフィールドを使用することが強く推奨されます。
SQL UPDATE もデータベースを攻撃するために使用されます。これらのク
エリも切捨てたり新しいクエリを元のクエリに追加することによる攻撃 を受けます。しかし、攻撃者は
SET
句を使用する可 能性があります。この場合、クエリを成功させるためにいくつかのスキー
マ情報を保有する必要があります。これは、フォームの変数名や総当た
り法により調べることができます。パスワードまたはユーザ名を保存す るフィールド用の命名記法はそう多くはありません。
// $uid == ' or uid like'%admin%'; -- $query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --"; // $pwd == "hehehe', admin='yes', trusted=100 " $query = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE ...;" |
恐ろしい例として、いくつかのデータベースホストのオペレーティン
グシステムレベルのコマンドがアクセス可能となる方法を示します。
$query = "SELECT * FROM products WHERE id LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--"; $result = mssql_query($query); |
注意 上記のいくつかの例は、データベースサーバの種類に依存しています。 これは、他の製品に対して同様な攻撃ができないことを意味するもので はありません。使用しているデータベースが他の手段で攻撃可能である 可能性もあります。
ほとんどの例では攻撃する者は特定のデータベースのスキーマに関して
若干の情報を保有している必要があると弁解されるかもしれません。
これは正しいですが、いつ何時これが外部にもれうるかを知ることはで
きませんし、いったんこの情報がもれると、データベースが外部に開示
される可能性があります。オープンソースまたは一般的に入手可能なデー
タベース処理パッケージを使用している場合(これはコンテンツ監理シス
テムまたはフォーラムに含まれている可能性があります)、侵入者は簡単
に使用されているコードの一部を入手することができます。そのコード
の設計が悪い場合にもセキュリティリスクを生じる可能性があります。
これらの攻撃は、セキュリティを考慮して書かれていないコードを攻撃
する方法です。特にクライアント側から入力されるあらゆる種類の入力
を決して信用しないで下さい。これは、selectボックスやhidden input
フィールド、Cookieの場合も同様です。最初の例は、このような欠点の
ないクエリが破滅をもたらしうることを示すものです。
データベースにスーパーユーザーまたはデータベースの所有者として
接続しないで下さい。 非常に制限された権限を有するカスタマイズ されたユーザを常に使用して下さい。
指定された入力が期待するデータ型であることを確認して下さい。
PHPは、多くの種類の入力検証用関数を有しており、
変数関連の関数
や
文字型関数
にある簡単な関数 (例: それぞれ、
is_numeric()
,
ctype_digit()
) や、
Perl互換の正規表現
のサポートま であります。
アプリケーションが、数値入力を期待している場合、データを
is_numeric()
で検証するか、
settype()
により暗黙の型変換を行うか、
sprintf()
により数値表現を使用することを検討 してみて下さい。
データベースに渡す数値以外のユーザ入力を
addslashes()
または
addcslashes()
でクオートして下さい。
最初の例
を参照 して下さい。前期の例が示すように、クエリの静的な部分をクオート
するだけでは充分ではなく、簡単にクラックされてしまう可能性があ ります。
データベース固有の情報、特にスキーマに関する情報は出力してはい きません。
エラー出力
およ び
エラー処理およびログ関数
も参照下さい。
ユーザがテーブルまたはビューに直接アクセスできないように、
データアクセスを抽象化することを目的としてストアドプロシージャ
及び事前に定義したカーソルを使用することもできますが、このソリュー ションには、副作用があります。
これらのケースにおいて、スクリプトまたはサポートされている場合は
データベース自体でクエリのログをとることが有益です。 明らかにログは破壊的な行為を防止することはできませんが、攻撃され
たアプリケーションを追跡する際には有効です。ログ自体は有益ではあ
りませんが、含まれている情報は有益です。通常、より詳細なログをと る方が良いでしょう。