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 WHEREvariety='lagrein'
4. クエリをMySQLサーバー。
5. MySQL は wines テーブル内のすべてのレコードを返します - その中で、フィールドの多様性の値は「lagrein」です。
もう、これはおなじみの非常に簡単なプロセスになっているはずです。残念ながら、私たちが慣れ親しんでいるプロセスが、簡単に自己満足につながることがあります。次に、作成したクエリを再分析してみましょう。
1. 作成するクエリの固定部分は一重引用符で終わり、変数値の始まりを説明するために使用します:
コードスニペットは次のとおりです:
$query = " SELECT * FROM wines WHERE variety = ' ";
2. ユーザー送信を含む変数の値で元の不変部分を使用します:
コード スニペットは次のとおりです:
$query .= $variety;
3. 次に、別の値を使用 この結果を連結するために一重引用符が使用されます - 変数値の終わりを説明します:
以下はコード スニペットです:
$ query .= "'"; したがって、$query の値は次のようになります。 :
以下はコード スニペットです:
SELECT * FROM wines WHEREvariation = '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 は、事前定義された安全なオプションではなく、不正なオプションを使用します。これを実現するのは非常に簡単です。ソース コードを見て、フォームのソース コードをカット アンド ペーストするだけです。そうすればすべてが開けます。
選択を変更した後、フォームを送信できるようになり、無効なコマンドは元のコマンドであるかのように受け入れられます。したがって、ユーザーはさまざまな方法を使用して、悪意のあるコードをスクリプトに挿入しようとする可能性があります。