PHP における SQL インジェクション攻撃の完全禁止の 1 つ
1. インジェクション攻撃の種類
攻撃の動機にはさまざまな種類があるかもしれませんが、一見すると、さらに種類があるように見えます。これは、悪意のあるユーザーが複数のクエリを実行するメソッドを発明した場合に当てはまります。これについては、この記事の後半で詳しく説明します。
スクリプトが SELECT 命令を実行している場合、攻撃者は、以下に示すように、「1=1」などの条件を WHERE 句に挿入することで、テーブル内のすべての行を強制的に表示させることができます (挿入された部分が示されています)太字):
SELECT * FROM wines WHEREvariation = 'lagrein' OR 1=1;'
前に説明したように、これはテーブルの一般的な構造を明らかにするため、それ自体が有益な情報になる可能性があります。 (普通の記録ではできないこと)、機密情報を含む記録を微妙に明らかにすることもできます。
アップデートされたコマンド待ち伏せには、より直接的な脅威が加わりました。 SET 句に他の属性を配置することで、攻撃者は現在更新されているレコードの任意のフィールドを変更できます。たとえば、次の例 (挿入された部分が太字で示されています):
UPDATE wines SET type= 'red', ' vintage'='9999' WHEREvariation = 'lagrein'
更新命令のWHERE句に1=1などのtrue条件を追加することで、この変更されたカテゴリを、次の例 (注入部分が太字で示されています):
UPDATE wines SET type='red', 'vintage'='9999 WHEREvariation = 'lagrein' OR 1=1 ;'
おそらく最も危険な命令は DELETE です。それは想像に難くありません。注入手法は、これまでに説明したものと似ています。次の例 (注入部分が太字で示されています) のように、WHERE 句を変更して影響を受けるレコードの範囲を拡張します。 'ラグレイン' OR 1=1;'
SELECT * FROM wines WHEREvariation = 'lagrein';
GRANT ALL ON *.* TO 'BadGuy@%' IDENTIFIED BY 'gotcha';'
このインジェクション新しいユーザー BadGuy を作成し、ネットワーク権限 (すべてのテーブルに対するすべての権限) を付与します。また、この単純な SELECT ステートメントには「unfortunate」パスワードも追加されます。前の記事のアドバイスに従ってプロセス ユーザーの権限を厳密に制限した場合、Web サーバー デーモンには取り消した GRANT 権限がなくなっているため、これは機能しません。しかし理論的には、このような攻撃により、BadGuy はデータベースに対してあらゆる制御を自由に実行できるようになる可能性があります。
このようなマルチクエリが MySQL サーバーによって処理されるかどうかについては、結論は一意ではありません。これは、MySQL のバージョンの違いが原因である場合もありますが、ほとんどの場合、複数のクエリの存在方法が原因です。 MySQL の監視プログラムでは、このようなクエリを完全に許可します。一般的に使用される MySQL GUI-phpMyAdmin は、最後のクエリの前に以前のすべてのコンテンツをコピーし、これのみを実行します。
ただし、インジェクションコンテキスト内のほとんどの複数のクエリは、PHP の mysql 拡張機能によって処理されます。幸いなことに、デフォルトでは、クエリ内で複数の命令を実行することはできません。2 つの命令 (上記のインジェクションなど) を実行しようとすると、エラーが設定されず、出力情報も得られません。この場合、PHP はデフォルトの動作を「段階的に」実装しているだけですが、ほとんどの単純なインジェクション攻撃からは保護されます。
PHP5 の新しい mysqli 拡張機能 (http://php.net/mysqli を参照) は、mysql と同様に、本質的に複数のクエリをサポートしませんが、複数のクエリの実装をサポートする mysqli_multi_query() 関数を提供します。 - 本当にそうしたいのなら。
ただし、PHP5 にバンドルされている埋め込み可能な SQL データベース エンジンである SQLite (http://sqlite.org/ および http://php.net/sqlite を参照) の場合、その使いやすさのため、状況はさらに悲惨です。アプリケーションは多くのユーザーの注目を集めています。場合によっては、データベースがバッチ クエリ、特にバッチ INSERT ステートメント処理を最適化でき、非常に効率的であるため、SQLite はデフォルトでこのような複数命令クエリを許可します。ただし、クエリの結果がスクリプトで使用される場合 (たとえば、レコードを取得するために SELECT ステートメントを使用する場合)、sqlite_query() 関数は複数のクエリの実行を許可しません。
3. INVISION Power BOARD SQLインジェクションの脆弱性
Invision Power Boardは有名なフォーラムシステムです。 2005 年 5 月 6 日、ログイン コードに SQL インジェクションの脆弱性が発見されました。発明者はGulfTech Security ResearchのJames Bercegay氏です。
このログインクエリは次のとおりです:
$DB->query('SELECT * FROM ibf_members WHERE id=$mid ANDpassword='$pid'');
このうち、メンバーID変数$midと、パスワード ID 変数 $pid は、次の 2 行のコードを使用して my_cookie() 関数から取得されます。 my_getcookie('pass_hash ');
ここで、my_cookie() 関数は次のステートメントを使用して、Cookie から要求された変数を取得します:
return urldecode($_COOKIE[$ibforums->vars['cookie_id'].$name ]);
【注意】このCookieから返される値は基本的に処理されません。 $mid はクエリに適用される前に整数に強制されますが、$pid は変更されません。したがって、前述したインジェクションタイプの攻撃に対して脆弱です。
したがって、my_cookie() 関数を次のように修正すると、この脆弱性が露呈します:
if ( ! in_array( $name, array('topicsread', 'forum_read', 'collapseprefs') ) )
{
return $this->
clean_value(urldecode($_COOKIE[$ibforums->vars['cookie_id'].$name]));
}
else
{
return urldecode($_COOKIE[ $ibforums->vars ['cookie_id'].$name]);
}
そのような修正の後、キー変数はグローバル clean_value() 関数を「通過」した後に返されますが、他の変数はチェックされません。
さて、SQL インジェクションとは何か、そのインジェクション原理、そしてこのインジェクションの脆弱性について一般的に理解できたので、それを効果的に防ぐ方法を見てみましょう。幸いなことに、PHP は豊富なリソースを提供するため、推奨する手法を使用して注意深く徹底的に構築されたアプリケーションは、スクリプトからの SQL の可能性を基本的に排除すると確信できます。これは、事前にユーザーのデータを「清算」することによって実現されます。損害を引き起こす可能性があります。
4. クエリ内のすべての値を定義します
クエリ内のすべての値を必ず定義することをお勧めします。最初に影響を受けるのは文字列値であり、通常 (「二重」引用符ではなく) 「一重引用符」を使用することが予想されるコンテンツと同様です。二重引用符を使用して PHP が文字列内の変数を交換できるようにすると、クエリの入力が容易になりますが、一方で、これにより (確かにほんのわずかですが) 将来の PHP の必要性も減ります。コード解析作業。
ここで、この問題を説明するために最初に使用した非注入クエリを使用してみましょう:
SELECT * FROM wines WHEREvariation = 'lagrein'
または、PHP ステートメントで次のように表現します:
$query = 'SELECT * FROM wines WHEREvariety = '$variety'';
技術的に言えば、数値に引用符は必要ありません。ただし、ワインなどのフィールドの値を引用符で囲んでも構わない場合、およびユーザーがフォームに null 値を入力した場合は、次のような内容が表示されます。 クエリ:
SELECT * FROM wines WHEREvintage =
もちろん、このクエリは構文的には無効ですが、次の構文は有効です:
SELECT * FROM wines WHEREvintage = ''
2 番目のクエリは (おそらく) 何も返しませんが、少なくともエラーは返しません。メッセージ。
5. ユーザーによって送信された値のタイプを確認します
前の説明から、これまでのところ、SQL インジェクションの重要な起源は、予期しないフォームのインポートから得られることが多いことがわかります。ただし、フォームを通じて特定の値を送信する機会をユーザーに提供する場合は、どのような入力を取得したいかをよく理解しておく必要があります。これにより、ユーザーの入力の有効性を確認しやすくなります。このような検証ヘッダーについては以前の記事ですでに説明したので、ここではそのときに説明した重要なポイントのみを簡単に要約します。数値を調べている場合は、次のいずれかの手法を使用して、実際に数値型を取得していることを確認できます。 is_int() 関数 (または is_integer() または is_long()) を使用します。
・gettype()関数を適用します。
・intval()関数を適用します。
・settype()関数を適用します。
ユーザー入力の長さを確認するには、strlen()関数を使用できます。希望の時刻または日付が有効かどうかを確認するには、strtotime() 関数を使用できます。これにより、ユーザーのインポートにセミコロン文字が含まれないことがほぼ確実になります (句読点を正当に含めることができる場合を除く)。以下に示すように、strpos() 関数を使用すると、これを簡単に実現できます:
前述したように、ユーザー入力の期待を慎重に分析している限り、存在する多くの問題を簡単に検出できるはずです。
6. クエリから疑わしい文字をすべて除外します
以前の記事で、危険な文字を含むタイトルを除外する方法について説明しましたが、ここでもう一度簡単に誇張して要約してみましょう。 ヒントは次のとおりです。 magic_quotes_gpc ディレクティブまたはその「舞台裏のパートナー」である addslashes() 関数を使用します。この関数はアプリケーション開発で制限されており、この関数では、stripslashes () 関数の使用も必要です。
・比較すると、mysql_real_escape_string() 関数がより一般的に使用されますが、これにも独自の欠点があります。
上記は、PHP における SQL インジェクション攻撃の完全禁止の第 2 部です。その他の関連コンテンツについては、PHP 中国語 Web サイト (www.php.cn) にご注意ください。