多くの有用な回答について、このスレッドに何らかの価値を追加したいと考えています。
SQL インジェクションは、ユーザー入力 (ユーザーによって入力され、クエリ内で使用される入力) を通じて実行できる攻撃です。SQL インジェクション パターンは正しいクエリ構文ですが、それを呼ぶこともできます: 悪い理由による悪いクエリであり、セキュリティの 3 つの原則 (機密性) に影響を与える秘密情報を取得しようとする (アクセス制御をバイパスする) 悪い人物が存在する可能性があると想定しています。 、整合性、および可用性)。
さて、私たちのポイントは、SQL インジェクション攻撃などのセキュリティの脅威を防ぐことです。質問 (PHP を使用して SQL インジェクション攻撃を防ぐ方法)、より現実的であること、データのフィルタリングまたは入力データのクリアは、内部でユーザー入力データを使用する場合です。 PHP やその他のプログラミング言語を使用したそのようなクエリは当てはまりません。または、プリペアド ステートメントなどの最新のテクノロジや、現在 SQL インジェクション防止をサポートしているその他のツールを使用することをより多くの人が推奨しているように、これらのツールはもう利用できないと考えてください。アプリケーションをどのように保護しますか?
SQL インジェクションに対する私のアプローチは、ユーザー入力データをデータベースに送信する前に (クエリ内で使用する前に) クリアすることです。
データのフィルタリング (安全でないデータを安全なデータに変換する)
PDOとMySQLiが利用できないことを考慮してください。アプリケーションをどのように保護できますか? それらを使用するように強制しますか?PHP以外の言語はどうですか?特定の言語だけでなく、より広い境界線に使用できるため、一般的なアイデアを提供することを好みます。
- SQL ユーザー (ユーザー特権の制限): 最も一般的な SQL 操作は (SELECT、UPDATE、INSERT) です。では、UPDATE 特権を必要としないユーザーになぜ UPDATE 特権を与えるのでしょうか? たとえば、ログイン ページと検索ページでは SELECT しか使用されていないのに、なぜこれらのページで高い権限を持つ DB ユーザーを使用するのでしょうか。
ルール: すべての権限に対して 1 つのデータベース ユーザーを作成しないでください。すべての SQL 操作で、(deluser、selectuser、updateuser) などのスキームをユーザー名として簡単に作成できます。
最小権限の原則を参照してください。
データのフィルタリング: クエリのユーザー入力を作成する前に、検証してフィルタリングする必要があります。プログラマーにとって、ユーザー入力変数ごとにいくつかのプロパティを定義することが重要です:
データ型、データ パターン、およびデータ長。(x と y) の間の数値であるフィールドは、正確なルールを使用して正確に検証する必要があり、文字列 (テキスト) であるフィールドの場合: パターンが該当します。たとえば、ユーザー名にはいくつかの文字のみを含める必要があります。 [a-zA-Z0-9_-.] と言います。長さは (x と n) の間で変化します。ここで、x と n (整数、x <=n) です。
ルール: 正確なフィルターと検証ルールを作成することは、私にとってベスト プラクティスです。
他のツールを使用する: ここでは、準備済みステートメント (パラメーター化されたクエリ) とストアド プロシージャについても同意します。ここでの欠点は、これらの方法には、ほとんどのユーザーには存在しない高度なスキルが必要なことです。ここでの基本的な考え方は、SQL クエリと内部で使用されるデータを区別することです。ここでのユーザー入力データは、(any または x=x) などの元のクエリに何も追加しないため、安全でないデータでも両方のアプローチを使用できます。
詳細については、OWASP SQL インジェクション防止チート シートを参照してください。
さて、あなたが上級ユーザーなら、この防御策を好きなように使い始めてください。しかし、初心者で、ストアド プロシージャをすぐに実装してステートメントを準備できない場合は、入力データをできるだけフィルタリングすることをお勧めします。
最後に、ユーザーがユーザー名を入力する代わりに、以下のテキストを送信したとします。
[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'
この入力は、プリペアド ステートメントやストアド プロシージャを使用せずに早い段階でチェックできますが、念のため、これらの使用はユーザー データのフィルタリングと検証の後に開始します。
最後のポイントは、より多くの労力と複雑さを必要とする予期しない動作の検出です。通常の Web アプリケーションにはお勧めしません。
上記のユーザー入力で予期しない動作が発生するのは、SELECT、UNION、IF、SUBSTRING、BENCHMARK、SHA、および root です。これらの単語が検出されたら、入力を避けることができます。
更新 1:
ユーザーがこの投稿は役に立たないとコメントしました。OK! OWASP.ORG が提供するものは次のとおりです。
主な防御:
オプション #1: プリペアド ステートメント (パラメーター化されたクエリ)
の使用 オプション #2: ストアド プロシージャの使用
オプション #3: すべてのユーザー提供の入力のエスケープ
追加の防御:
また実施: 最小特権
また実行: ホワイト リスト入力検証
ご存知かもしれませんが、論文の主張は有効な議論、少なくとも 1 つの参考文献によって裏付けられる必要があります。そうでなければ、それは攻撃と悪い主張と見なされます!
更新 2:
PHPマニュアルから、PHP: Prepared Statements - Manual :
エスケープと SQL インジェクション
バインドされた変数は、サーバーによって自動的にエスケープされます。サーバーは、エスケープされた値を実行前にステートメント テンプレートの適切な場所に挿入します。適切な変換を作成するには、バインドされた変数の型のヒントをサーバーに提供する必要があります。詳細については、mysqli_stmt_bind_param() 関数を参照してください。
サーバー内での値の自動エスケープは、SQL インジェクションを防ぐためのセキュリティ機能と見なされることがあります。入力値が正しくエスケープされていれば、準備されていないステートメントでも同じ程度のセキュリティを実現できます。
更新 3:
PDO と MySQLi がプリペアド ステートメントを使用して MySQL サーバーにクエリを送信する方法を知るためのテスト ケースを作成しました。
PDO:
$user = "''1''"; // Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));
クエリ ログ:
189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
189 Quit
MySQL:
$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();
クエリ ログ:
188 Prepare SELECT * FROM awa_user WHERE username =?
188 Execute SELECT * FROM awa_user WHERE username ='\'\'1\'\''
188 Quit
準備されたステートメントもデータをエスケープしていることは明らかです。
上記の発言にもあるように、
サーバー内での値の自動エスケープは、SQL インジェクションを防ぐためのセキュリティ機能と見なされることがあります。入力値が正しくエスケープされている場合、準備されていないステートメントでも同じ程度のセキュリティを実現できます
intval()
したがって、これは、クエリを送信する前に整数値に対してなどのデータ検証を行うことをお勧めします。さらに、クエリを送信する前に悪意のあるユーザー データを防止することは、正しく有効なアプローチです。
詳細については、この質問を参照してください: PDO は生のクエリを MySQL に送信し、Mysqli は準備されたクエリを送信します。どちらも同じ結果を生成します。
参考文献:
- SQL インジェクションチートシート
- SQL インジェクション
- 情報セキュリティー
- セキュリティ原則
- データ検証