本連載では、PHP開発環境におけるSQLインジェクション攻撃を完全に禁止する方法と、具体的な開発例を網羅的に解説していきます。
1. はじめに
PHP は強力ですが、習得が簡単なサーバーサイド スクリプト言語であり、経験の浅いプログラマでも、それを使用して複雑な動的 Web サイトを作成できます。ただし、インターネット サービスの機密性とセキュリティを実現するには、多くの困難が伴います。この一連の記事では、Web 開発に必要なセキュリティの背景と、PHP 固有の知識とコードを読者に紹介します。これを使用して、独自の Web アプリケーションのセキュリティと一貫性を保護できます。まず、サーバーのセキュリティ問題について簡単に説明します。共有ホスティング環境で個人情報にアクセスする方法、開発者を運用サーバーにアクセスさせない方法、ソフトウェアを最新の状態に保つ方法、暗号化されたチャネルを提供する方法、およびシステム アクセスへのアクセスを制御する方法を示します。
次に、PHP スクリプト実装における広範な脆弱性について説明します。 SQL インジェクションからスクリプトを保護し、クロスサイト スクリプティングとリモート実行を防止し、一時ファイルとセッションの「ハイジャック」を無効にする方法について説明します。
最後の記事では、安全な Web エクスプロイトを実装します。ユーザーの認証、アプリケーションの承認と追跡、データ損失の回避、高リスクのシステム コマンドを安全に実行する方法、Web サービスを安全に使用する方法を学びます。 PHP セキュリティ開発の十分な経験があるかどうかに関係なく、このシリーズの記事は、より安全なオンライン アプリケーションを構築するのに役立つ豊富な情報を提供します。
2. SQL インジェクションとは何ですか
特定のデータを決して使用しない予定がある場合、データベースの設計目標はデータベース データに簡単にアクセスして操作することであるため、それらをデータベースに保存することに意味はありません。しかし、これを安易に行うと、潜在的な災害につながる可能性があります。あなた自身がデータベース内のすべてを誤って削除してしまう可能性があるため、この状況は重要ではありません。それは、あなたが何らかの「無害な」タスクを完了しようとしている間に、誰かによって「ハイジャック」される可能性があるためです。つまり、アプリケーション自体が自分のデータを置き換えるためにデータを破損しました。データ。この置換を「注入」と呼びます。
実際、データベース クエリを構築するためにユーザーに入力を求めるたびに、データベース サーバーにアクセスするコマンドの構築にユーザーが参加できるようになります。友好的なユーザーはそのような制御を達成することに満足するかもしれませんが、悪意のあるユーザーはコマンドを捻じ曲げる方法を発明しようとし、その捻じ曲げられたコマンドによってデータが削除されたり、さらに危険なことさえ行われたりすることがあります。プログラマーとしてのあなたの仕事は、このような悪意のある攻撃を回避する方法を見つけることです。
3. SQL インジェクションの仕組み
データベースクエリの構造化は非常に簡単なプロセスです。通常、これは次の方針に沿って実装されます。タイトルを説明するために、フィールド「品種」(すなわち、ワインの種類) を持つワインデータベーステーブル「ワイン」があると仮定します。
1. フォームを提供する - ユーザーがコンテンツを検索するために何かを送信できるようにします。 。ユーザーがタイプ「ラグレイン」のワインを検索することを選択したと仮定します。
2. ユーザーの検索語を取得して保持します - 次のように変数に割り当てます:
$variety = $_POST['variety'];
したがって、変数 $variety の値は今:
lagrein
3. 次に、この変数を使用して、WHERE 句でデータベース クエリを構造化します。
$query = 'SELECT * FROM wines WHEREvarie='$variety';
したがって、変数 $query は次のようになります:
SELECT * FROM wines WHEREvariant='lagrein'
4. クエリを MySQL サーバーに送信します。
5. MySQL は wines テーブル内のすべてのレコードを返します - その中で、フィールドの多様性の値は「lagrein」です。
今では、これはおなじみの簡単なプロセスになっているはずです。残念ながら、私たちが慣れ親しんで快適に感じているプロセスが、簡単にプライドの感情につながることがあります。次に、作成したクエリを再分析してみましょう。
1. 作成するクエリの固定部分は一重引用符で終わり、変数値の始まりを説明するために使用します:
$query = ' SELECT * FROM wines WHEREvariation = '';
2 . アプリケーション 元の固定部分には、ユーザーが送信した変数の値が含まれます:
$query .= $variety;
3. 次に、別の一重引用符を使用して変数値の終わりを記述します。 $ query .= ''';
したがって、$queryの値は次のようになります:
SELECT * FROM wines WHEREvariation = 'lagrein'
この構造が成功するかどうかは、ユーザーの入力に依存します。この例では、単一の単語 (場合によっては単語のグループ) を使用してワインの種類を指定しています。したがって、クエリはヘッダーなしで作成され、結果は期待どおり、ワインの種類が「lagrein」であるワインのリストになります。ここで、タイプ 'lagrein' という単純なワインの種類を入力する代わりに、ユーザーが次のように入力すると想像してみましょう (2 つの句読点が含まれていることに注意してください):
lagrein' or 1= 1;
さて、あなたは引き続きクエリを構造化するために以前に修正した部分 (ここでは、$query 変数の結果値のみを示します): SELECT * FROM wines WHEREvariation = '
次に、次の内容を使用します。ユーザーが入力した変数の値は次のように連結されます。それ (ここでは太字で示しています):
SELECT * FROM wines WHEREvariation = 'lagrein' or 1=1;
最後に、上下の引用符を追加します:
SELECT * FROM wines WHEREvariation = 'lagrein' or 1 =1;'
したがって、このクエリの結果は、予想したものとはかなり異なります。実際、ユーザーが入力した最後のセミコロンが最初の命令 (レコードの選択) を終了し、新しい命令を開始しているため、クエリには 1 つではなく 2 つの命令が含まれています。この場合、2 番目の命令には単純な一重引用符以外の意味はありませんが、最初の命令も実装したいものではありません。ユーザーが入力の途中に一重引用符を入れると、変数の値を調べて別の条件を導入することになります。したがって、多様性が 'lagrein' であるレコードを取得する代わりに、2 つの基準のいずれかを満たすレコードを取得します (1 つ目はあなたのもの、2 つ目は彼のものです。多様性は 'lagrein' または 1、つまり 1 のレコードです)。 1 は常に 1 なので、すべてのレコードを取得することになります。
ユーザーが送信した変数を記述するには一重引用符の代わりに二重引用符を使用する必要があるのではないか、という反論があるかもしれません。はい、これにより少なくとも悪意のあるユーザーからの攻撃を遅らせることができます。 (前の記事で、ユーザーへのすべてのエラー通知メッセージを無効にする必要があることを思い出させました。ここでエラー メッセージが生成された場合、攻撃者にとって役立つ可能性があります。攻撃が失敗した理由についての説明が提供されます。具体的な手順)
実際には、ユーザーが一部のレコードだけでなくすべてのレコードを参照できるようにするのは、最初は面倒に思えるかもしれませんが、実際には、テーブルの内部構造を簡単に参照できるようになります。さらに邪悪な目的のための重要な参考資料を彼に提供することになる。データベースに、アルコールに関する一見無害な情報ではなく、従業員の年間収入のリストが含まれている場合、今説明した状況は特に当てはまります。
理論的な観点から見ると、この種の攻撃は確かにひどいことです。予期しないコンテンツをクエリに挿入することで、このユーザーはデータベース アクセスを自分自身の目標の達成に変えることができます。これで、あなたのデータベースは、あなたにとっても同様に、彼に対しても開かれることになります。
IV. PHP と MySQL インジェクション
前に説明したように、PHP は独自の設計により、指示に従って動作することを除いて、特別なことは何も行いません。したがって、悪意のあるユーザーが使用した場合、前述したような、要求に応じて特別に設計された攻撃にのみ「同意」することになります。
有害な結果をもたらすデータベース クエリを意図的に、または偶発的に構築することはないと仮定します。そのため、問題はユーザーからの入力にあると仮定します。ここで、ユーザーがスクリプトに情報を提供できるさまざまな方法を詳しく見てみましょう。
5. ユーザー入力の種類
現在、スクリプトに影響を与えるためにユーザーが実行できるアクションはますます複雑になっています。
ユーザー入力の最も明白なソースは、もちろんフォーム上のテキスト入力フィールドです。このようなフィールドを使用すると、文字通りユーザーに任意のデータの入力を促すことになります。さらに、ユーザーに広い入力範囲を提供するため、ユーザーが入力できるデータの種類を事前に制限する方法はありません (ただし、その長さを制限することはできます)。これが、ほとんどのインジェクション攻撃が防御されていないフォーム フィールドから発生する理由です。
しかし、攻撃源は他にもあり、少し考えてみると、フォームの背景に隠されたテクニック、POSTメソッドが思い浮かぶでしょう!ブラウザのナビゲーション ツールバーに表示される URI を簡単に分析することで、注意力のあるユーザーはスクリプトにどのような情報が渡されるかを簡単に確認できます。このような URI は通常、プログラムによって生成されますが、悪意のあるユーザーが単に不適切な変数値を含む URI をブラウザーに入力することを防ぐ手段はなく、悪用される可能性のあるデータベースを開こうとする可能性があります。
ユーザー入力を制限するための一般的な戦略は、入力ボックスの代わりにフォームに選択ボックスを提供することです。この種のコントロールを使用すると、事前に定義された値のセットからユーザーを選択するよう強制でき、ある程度表示されないコンテンツをユーザーが入力できないようにすることができます。しかし、攻撃者が URI を「スプーフィング」する (つまり、信頼できるが無効な URI を模倣する URI を作成する) のと同じように、フォームになりすまして独自のバージョンを作成し、その結果、新しいバージョンの URI を作成することもできます。オプションボックスを使用して、事前定義された安全な選択の代わりに不正な選択を適用します。これを行うのは非常に簡単です。ソース コードを見て、フォームのソース コードをカット アンド ペーストするだけです。そうすれば、扉が開きます。
選択を修正すると、フォームを送信できるようになり、無効なコマンドが元のコマンドであるかのように受け入れられるようになります。したがって、ユーザーはさまざまな方法を使用して、スクリプトに悪意のあるコードを挿入しようとする可能性があります。
上記は、PHP における SQL インジェクション攻撃を完全に禁止する内容の 1 つです。その他の関連内容については、PHP 中国語 Web サイト (www.php.cn) にご注意ください。