多くの有用な回答に関して、私はこのスレッドにいくつかの価値を追加したいと思っています。
SQLインジェクションは、ユーザー入力(ユーザーによって入力され、クエリ内で使用される入力)を介して実行できる攻撃です。SQLインジェクションパターンは正しいクエリ構文ですが、それは私たちが呼び出すことができます。悪い理由による悪いクエリであり、セキュリティの3つの原則(機密性)に影響を与える(アクセス制御をバイパスして)秘密の情報を取得しようとする悪い人がいる可能性があると想定しています、整合性、可用性)。
ここでのポイントは、SQLインジェクション攻撃、質問(PHPを使用したSQLインジェクション攻撃を防ぐ方法)などのセキュリティ上の脅威を防ぐこと、より現実的になることです。内部でユーザー入力データを使用する場合は、データフィルタリングまたは入力データのクリアが当てはまります。そのようなクエリは、PHPやその他のプログラミング言語を使用していないか、または準備済みステートメントやSQLインジェクション防止を現在サポートしているその他のツールなどの最新テクノロジーを使用するように多くの人々から推奨されているため、これらのツールはもう使用できないと考えていますか?アプリケーションをどのように保護しますか?
SQLインジェクションに対する私のアプローチは、ユーザー入力データをデータベースに送信する前に(クエリ内で使用する前に)クリアすることです。
データのフィルタリング(安全でないデータを安全なデータに変換)
PDOとMySQLiが利用できないことを考慮してください。アプリケーションをどのように保護できますか?それらを使用するように強制しますか?PHP以外の他の言語についてはどうですか?特定の言語だけでなく、より広い国境で使用できるため、私は一般的なアイデアを提供することを好みます。
- SQLユーザー(ユーザー特権の制限):最も一般的なSQL操作は(SELECT、UPDATE、INSERT)ですが、なぜそれを必要としないユーザーに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
MySQLi:
$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インジェクション
- 情報セキュリティー
- セキュリティ原則
- データ検証