セキュリティリスク
データベースには機密データが含まれていることが多く、危険な操作を実行できます。Nette Databaseを安全に使用するためには、以下が重要です:
- 安全なAPIと危険なAPIの違いを理解する
- パラメータ化されたクエリを使用する
- 入力データを正しく検証する
SQLインジェクションとは?
SQLインジェクションは、データベースを操作する上で最も深刻なセキュリティリスクです。これは、ユーザーからの未処理の入力がSQLクエリの一部になったときに発生します。攻撃者は独自のSQLコマンドを挿入し、それによって:
- データへの不正アクセスを取得する
- データベース内のデータを変更または削除する
- 認証を回避する
これはDatabase Explorerにも当てはまります:
パラメータ化されたクエリ
SQLインジェクションに対する基本的な防御策は、パラメータ化されたクエリです。Nette Databaseは、それらを使用するためのいくつかの方法を提供します。
最も簡単な方法は、疑問符プレースホルダを使用することです:
これは、疑問符プレースホルダとパラメータを含む式を挿入できるDatabase Explorerの他のすべてのメソッドに適用されます。
INSERT、UPDATEコマンド、またはWHERE句の場合、値を配列で渡すことができます:
パラメータ値の検証
パラメータ化されたクエリは、データベースを安全に操作するための基本的な構成要素です。ただし、それらに挿入する値は、いくつかのレベルのチェックを通過する必要があります:
型チェック
最も重要なのは、パラメータの正しいデータ型を保証することです – これはNette Databaseを安全に使用するための必須条件です。データベースは、すべての入力データが特定のカラムに対応する正しいデータ型を持っていることを前提としています。
たとえば、前の例で $name
が文字列ではなく予期せず配列であった場合、Nette
Databaseはそのすべての要素をSQLクエリに挿入しようとし、エラーが発生します。したがって、決して
$_GET
、$_POST
、または $_COOKIE
からの未検証のデータをデータベースクエリで直接使用しないでください。
フォーマットチェック
第2レベルでは、データのフォーマットをチェックします – たとえば、文字列がUTF-8エンコーディングであり、その長さがカラム定義に対応しているか、または数値が特定のカラムデータ型で許可されている範囲内にあるかどうか。
このレベルの検証では、データベース自体にも部分的に依存できます – 多くのデータベースは無効なデータを拒否します。ただし、動作は異なる場合があり、一部は長い文字列を黙って切り捨てたり、範囲外の数値を切り捨てたりする場合があります。
ドメインチェック
第3レベルは、アプリケーション固有の論理チェックを表します。たとえば、セレクトボックスの値が提供されたオプションに対応していること、数値が期待される範囲内にあること(例:年齢0〜150歳)、または値間の相互依存関係が意味をなすことの検証。
推奨される検証方法
- すべての入力の正しい検証を自動的に保証するNette Formsを使用します
- Presentersを使用し、
action*()
およびrender*()
メソッドのパラメータにデータ型を指定します - または、
filter_var()
などの標準的なPHPツールを使用して独自の検証層を実装します
カラムの安全な操作
前のセクションでは、パラメータ値を正しく検証する方法を示しました。ただし、SQLクエリで配列を使用する場合、そのキーにも同じ注意を払う必要があります。
INSERTおよびUPDATEコマンドの場合、これは重大なセキュリティエラーです –
攻撃者はデータベース内の任意のカラムを挿入または変更できます。たとえば、is_admin = 1
を設定したり、機密カラムに任意のデータを挿入したりできます(いわゆるマスアサインメント脆弱性)。
WHERE条件では、演算子を含めることができるため、さらに危険です:
攻撃者はこのアプローチを使用して、従業員の給与を体系的に特定できます。たとえば、100,000を超える給与のクエリから始め、次に50,000未満のクエリを行い、範囲を徐々に狭めることで、すべての従業員のおおよその給与を明らかにすることができます。このタイプの攻撃はSQL列挙と呼ばれます。
where()
およびwhereOr()
メソッドは、さらに柔軟であり、キーと値に演算子や関数を含むSQL式をサポートしています。これにより、攻撃者はSQLインジェクションを実行できます:
この攻撃は、0)
を使用して元の条件を終了し、UNION
を使用して独自のSELECT
を追加してusers
テーブルから機密データを取得し、WHERE (1)
を使用して構文的に正しいクエリを閉じます。
カラムのホワイトリスト
カラム名を安全に操作するには、ユーザーが許可されたカラムのみを操作でき、独自のカラムを追加できないようにするメカニズムが必要です。危険なカラム名を検出してブロックしようとする(ブラックリスト)こともできますが、このアプローチは信頼できません – 攻撃者は常に、予測していなかった危険なカラム名を記述する新しい方法を見つけることができます。
したがって、ロジックを逆にして、許可されたカラムの明示的なリスト(ホワイトリスト)を定義する方がはるかに安全です:
動的識別子
テーブル名とカラム名を動的に指定するには、プレースホルダ ?name
を使用します。これにより、特定のデータベースの構文に従って識別子が正しくエスケープされます(たとえば、MySQLではバッククォートを使用):
重要:シンボル ?name
は、アプリケーションコードで定義された信頼できる値にのみ使用してください。ユーザーからの値には、再びホワイトリストを使用してください。そうしないと、セキュリティリスクにさらされます: