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