[転送]SQLインジェクション
SQLインジェクション
多くの Web 開発者は、SQL クエリが改ざんされる可能性があることに気づいていないため、SQL クエリを信頼できるコマンドとして扱っています。彼らは、SQL クエリがアクセス制御をバイパスし、それによって認証と権限のチェックをバイパスできることをほとんど知りませんでした。さらに、SQL クエリを介してホスト オペレーティング システム レベルのコマンドを実行することもできます。
ダイレクト SQL コマンド インジェクションは、攻撃者が一般的に使用する手法で、既存の SQL ステートメントを作成または変更して、隠しデータを取得したり、キー値を上書きしたり、データベース ホスト オペレーティング システム コマンドを実行したりすることもあります。これは、アプリケーションがユーザー入力を取得し、それを静的パラメーターと組み合わせて SQL クエリにすることによって実現されます。いくつかの実例を以下に示します。
入力されたデータの検証が不足しており、新しいユーザーを作成する権限を持つスーパーユーザーまたは他のデータベース アカウントを使用した接続が原因で、攻撃者はデータベースに新しいスーパーユーザーを作成する可能性があります。
例#1 データのページング表示を実装するコードは、スーパーユーザー (PostgreSQL システム) の作成にも使用できます。
PHP コンテンツをクリップボードにコピーします
PHPコード:
$offset = $argv[0]; // 入力検証は行われないことに注意してください。
$query = "商品から ID、名前を選択 名前で注文 LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);
一般に、ユーザーは $offset がビニングされている「前のページ」リンクと「次のページ」リンクをクリックします。元のコードでは、$offset が数値であるとのみ考えられます。ただし、誰かが最初に次のステートメントを urlencode() してから URL に追加しようとした場合:
0;
pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)に挿入
「クラック」、usesysid、「t」、「t」、「クラック」を選択します
pg_shadow から usename='postgres';
--
その後、スーパー ユーザーを作成できます。 0; は、エラーが発生しないように、元のクエリを完成させるための正しいオフセットを提供するだけであることに注意してください。
注: -- は SQL のコメント マークであり、通常、SQL インタープリターに次のステートメントを無視するように指示するために使用されます。
パスワードを取得する考えられる方法は、検索結果を表示するページをターゲットにすることです。攻撃者が行う必要があるのは、どの変数が SQL ステートメントに送信され、それらの変数が誤って処理されたかを特定することだけです。このような変数は通常、WHERE、ORDER BY、LIMIT、OFFSET などの SELECT クエリの条件文で使用されます。データベースが UNION 構造をサポートしている場合、攻撃者は完全な SQL クエリを元のステートメントに追加して、任意のデータ テーブルからパスワードを取得する可能性もあります。したがって、パスワードフィールドを暗号化することが重要です。
例#2 記事といくつかのパスワードを表示します (任意のデータベース システム)
PHP コンテンツをクリップボードにコピーします
PHPコード:
$query = "製品から ID、名前、挿入されたサイズを選択します
WHERE サイズ = '$size'
$order LIMIT で注文 $limit, $offset;";
$result = odbc_exec($conn, $query);
元のクエリに別の SELECT クエリを追加して、パスワードを取得できます:
Union select '1'、concat(uname||'-'||passwd) を名前として、'1971-01-01'、'0' をユーザーテーブルから選択します;
--
上記のステートメント (' と -- を使用) を $query 内の変数に追加すると、問題が発生します。
SQL の UPDATE にも脆弱性があります。このクエリは、上の例のように、別の完全なリクエストに挿入または追加することもできます。しかし、攻撃者は、テーブル内の一部のデータを変更できるように、SET 句をターゲットにすることを好みます。この場合、クエリを正常に変更するには、データベースの構造を知っている必要があります。フォーム上の変数名に基づいてフィールドを推測したり、総当たりでクラックしたりすることができます。ユーザー名とパスワードを保存するフィールドに名前を付ける方法は多くありません。
例#3 パスワードのリセットから…より多くの権限の取得まで (任意のデータベース システム)
PHP コンテンツをクリップボードにコピーします
PHPコード:
$query = "UPDATE ユーザーテーブル SET pwd='$pwd' WHERE uid='$uid';";
しかし、悪意のあるユーザーは、変数の値として ' または '%admin%'; のような uid を $uid に送信するか、$pwd の値を "hehehe", admin='yes' として送信します。 、trusted=100 "(その後にスペースを付ける) と追加の権限を取得します。これを行うと、クエリ ステートメントは実際には次のようになります:
PHP コンテンツをクリップボードにコピーします
PHPコード:
// $uid == ' または '%admin%' のような uid;
$query = "UPDATE usertable SET pwd='...' WHERE uid='' または '%admin%' のような uid; --";
// $pwd == "ふふふ、admin='はい'、trusted=100 "
$query = "UPDATE usertable SET pwd='hehehe'、admin='yes'、trusted=100 WHERE
...;";
次の恐ろしい例は、一部のデータベースでシステム コマンドを実行する方法を示しています。
例#4 データベースが配置されているホストのオペレーティング システム (MSSQL サーバー) を攻撃します
PHP コンテンツをクリップボードにコピーします
PHPコード:
$query = "SELECT * FROM products WHERE ID LIKE '%$prod%'";
$result = mssql_query($query);
攻撃が変数 $prod の値として %' exec master..xp_cmdshell 'net user test testpass /ADD' を送信すると、$query は
になります。
PHP コンテンツをクリップボードにコピーします
PHPコード:
$query = "製品から * を選択
「%a%」のような場所 ID
exec master..xp_cmdshell 'net user test testpass /ADD'--";
$result = mssql_query($query);
MSSQL サーバーは、システムにユーザーを追加するために続くコマンドを含む、この SQL ステートメントを実行します。このプログラムが sa として実行されており、MSSQLSERVER サービスに十分な権限がある場合、攻撃者はホストにアクセスするためのシステム アカウントを取得できます。
注: 上記の例は特定のデータベース システムに関するものですが、他のデータベース システムに対して同様の攻撃を実行できないという意味ではありません。さまざまな方法を使用すると、さまざまなデータベースが影響を受ける可能性があります。
予防措置
攻撃者は上記の攻撃を実行するためにデータベース構造に関する情報を知る必要があると言って自分を慰める人もいるかもしれません。はい、そうです。しかし、攻撃者がこの情報を取得しないとは誰も保証できません。取得したデータベースは漏洩の危険にさらされます。フォーラム プログラムなどのオープン ソース ソフトウェア パッケージを使用してデータベースにアクセスしている場合、攻撃者が関連コードを入手するのは簡単です。コードの設計が不適切な場合、リスクはさらに大きくなります。
これらの攻撃は常に、セキュリティを考慮していないコードの悪用に基づいています。したがって、外部から入力されたデータ、特にクライアントから入力されたデータ (選択ボックス、フォームの隠しフィールド、Cookie など) を決して信頼しないでください。上記の最初の例と同様、通常のクエリでも災害が発生する可能性があります。
データベースへの接続にはスーパーユーザーまたは所有者アカウントを決して使用しないでください。権限が厳しく制限されたアカウントを使用してください。
入力データが予期したデータ形式であるかどうかを確認します。 PHP には、単純な変数関数や文字型関数 (is_numeric()、ctype_digit() など) から、この仕事を実行できる複雑な Perl 互換の正規表現関数まで、入力のチェックに使用できる関数が多数あります。
プログラムが数値の入力を待機している場合は、 is_numeric() を使用して数値をチェックするか、 settype() を直接使用してその型を変換するか、 sprintf() を使用して数値としてフォーマットすることを検討してください。
Example#5 ページングを実装するより安全な方法
PHP コンテンツをクリップボードにコピーします
PHPコード:
settype($offset, 'integer');
$query = "商品から ID、名前を選択 名前で注文 LIMIT 20 OFFSET $offset;";
// フォーマット文字列の %d に注意してください。%s を使用すると意味がありません
$query = sprintf("商品から ID、名前を選択 名前順に注文 LIMIT 20 OFFSET %d;",
$offset);
データベース固有の機密文字エスケープ関数 (mysql_escape_string() や sql_escape_string() など) を使用して、ユーザーが送信した非数値データをエスケープします。データベースに特別な機密文字エスケープ関数がない場合は、代わりに addslashes() と str_replace() を使用してこの作業を完了できます。最初の例を見ると、この例は、クエリの静的部分を引用するだけでは十分ではなく、クエリが簡単に壊れる可能性があることを示しています。
データベース、特にデータベースの構造については、絶対に自信を示さないようにしてください。エラー報告機能とエラー処理機能を参照してください。
ユーザーがデータ テーブルやビューに直接アクセスできないように、データベース ストアド プロシージャや抽象データベース アクセスへの定義済みポインタなどの機能を使用することも選択できます。しかし、このアプローチには別の意味もあります。
これに加えて、可能な場合はコードまたはデータベース システムを使用してクエリ ログを保存することもお勧めします。明らかに、ログは攻撃を防ぐものではありませんが、どのプログラムが攻撃を試みたかを追跡するために使用できます。ログ自体は役に立ちません。ログに含まれる情報を参照する必要があります。結局のところ、情報は無いよりはあった方が良いのです。