1. セキュリティ抽象化レイヤーを作成する
ユーザー入力のすべてのインスタンスに上記の手法を手動で適用することはお勧めしませんが、この目的のために抽象化レイヤーを作成することを強くお勧めします。単純な抽象化は、検証計画を関数に追加し、ユーザーが入力した項目ごとにこの関数を呼び出すことです。もちろん、より複雑で高レベルの抽象化、つまり安全なクエリをすべてのアプリケーションで使用できるクラスにカプセル化することもできます。オンラインで利用できるこのような既製の無料クラスが多数あります。この記事では、そのうちのいくつかについて説明します。
この抽象化を行うことには少なくとも 3 つの利点があります (そしてそれぞれセキュリティ レベルが向上します):
1. ローカライズされたコード。
2. クエリの構造をより高速かつ信頼性の高いものにします。これにより、作業の一部を抽象コードにオフロードできるためです。
3. セキュリティ機能を念頭に置いて構築され、適切に適用されると、先ほど説明したさまざまなインジェクション攻撃を効果的に防ぐことができます。
2. 既存のアプリケーションを改善する
既存のアプリケーションを改善したい場合は、単純な抽象化レイヤーを使用するのが最も適切です。収集したユーザー入力を単純に「サニタイズ」する関数は次のようになります:
function safety( $string ) {
return ''' . mysql_real_escape_string( $string ) '''
}
【注意】構築しました。一重引用符は、値のリクエストと mysql_real_escape_string() 関数に対応します。次に、この関数を使用して、以下に示すように $query 変数を構築できます:
$variety = safety( $_POST['variety'] );
$query = ' SELECT * FROM wines WHERE $variety=' 。 ;
ここで、ユーザーは変数 $variety の値として次のように入力して、インジェクション攻撃を実行しようとします:
lagrein' または 1=1;
上記の「清算」が実行されない場合、最終的なクエリは次のようになります (予測できない結果が生じます):
SELECT * FROM wines WHEREvariation = 'lagrein' or 1=1;'
しかし、ユーザーの入力がクリアされているため、クエリのステートメントは次のようになります。次の無害な形式:
SELECT * FROM wines WHERE 品種 = 'lagrein' または 1=1;'
指定された値に対応する品種フィールドがデータベースにないため (これは悪意があります。ユーザーが入力するものは -lagrein'または 1=1;)、このクエリは結果を返さず、インジェクションは失敗します。
3. 新しいアプリケーションを保護する
新しいアプリケーションを作成する場合は、セキュリティ抽象化レイヤーを最初から作成できます。現在、PHP 5 で新しく改善された MySQL のサポート (主に新しい mysqli 拡張機能に反映されています) により、このセキュリティ機能 (手続き型とオブジェクト指向の両方) が強力にサポートされます。 mysqli に関する情報は、サイト http://php.net/mysqli から入手できます。この mysqli サポートは、--with-mysqli=path/to/mysql_config オプションを使用して PHP をコンパイルした場合にのみ利用可能であることに注意してください。これは、mysqli ベースのクエリを保護するために使用される、このコードの手続き型バージョンです:
<?php
//ユーザーの入力を取得します
$animalName = $_POST['animalName'];
//データベースに接続します
$connect = mysqli_connect( 'localhost', 'username', 'password', ' database' );
if ( !$connect ) exit( 'connection failed: ' . mysqli_connect_error() );
//クエリ文sourceを作成します
$stmt = mysqli_prepare( $connect,'SELECT Intelligence FROM Animals WHERE name = ? ' );
if ( $stmt ) {
//交換をステートメントにバインド
mysqli_stmt_bind_param( $stmt, 's', $animalName );
// ステートメントを実行
mysqli_stmt_execute( $stmt );
// 取得results...
mysqli_stmt_bind_result($stmt, $intelligence);
// ...そして表示します
if ( mysqli_stmt_fetch( $stmt ) ) {
print 'A $animalName has $intelligence Intelligence.n';
} else {
print 'Sorry, no records found.';
}
//ステートメントソースをクリア
mysqli_stmt_close( $stmt );
}
mysqli_close( $connect );
?>
この mysqli 拡張機能は、次の関数のセットを提供します。クエリの構築と実行。さらに、これまで独自のsafe()関数を使用して実現していた機能も非常に正確に提供されます。
上記のスニペットでは、ユーザーが送信した入力コンテンツが最初に収集され、データベース接続が確立されます。次に、mysqli_prepare() 関数を使用してクエリ ソースを作成します。ここでは、クエリ ソースを使用する関数の名前を反映するために $stmt という名前を付けています。この関数は、接続リソースと文字列 (展開を使用して値を挿入するたびに「?」マークが挿入されます) という 2 つのパラメータを取ります。この場合、そのような値は動物の名前 1 つだけです。
SELECT ステートメントでは、「?」マークを配置できる唯一の場所は値の比較部分であることに注意してください。これが、適用する変数を指定する必要がない理由です (mysqli_stmt_bind_param() 関数を除く)。ここでは、そのタイプも指定する必要があります。この場合、「s」は文字列を表します。その他の可能なタイプは、整数の場合は「I」、倍精度浮動小数点 (または浮動小数点) の場合は「d」、バイナリ文字列の場合は「b」です。
関数 mysqli_stmt_execute()、mysqli_stmt_bind_result()、および mysqli_stmt_fetch() は、クエリの実行と結果の取得を担当します。検索結果が存在する場合はそれを表示し、結果が存在しない場合は無害なメッセージを表示します。最後に、$stmt リソースとデータベース接続を閉じ、メモリから解放する必要があります。
正当なユーザーが文字列「レミング」を入力したと仮定すると、このルーチンは(データベースに適切なデータがあると仮定して)「レミングの知能は非常に低いです。」というメッセージを出力します。一時的なインジェクションがあると仮定すると (たとえば、「lemming」または 1=1;)、このルーチンは (無害な) メッセージ「Sorry, no records found.」を出力します。
さらに、mysqli 拡張機能は、同じルーチンのオブジェクト指向バージョンも提供します。以下では、このバージョンがどのように適用されるかを明確にしたいと思います。
<?php
$animalName = $_POST['animalName'];
$mysqli = new mysqli( 'localhost', 'username', 'password', 'database');
if ( !$mysqli ) exit( '接続に失敗しました: ' . mysqli_connect_error() );
$stmt = $mysqli->prepare( 'SELECT Intelligence
FROM 動物 WHERE name = ?' );
if ( $stmt ) {
$stmt->bind_param( 's', $animalName );
$stmt->execute();
$stmt->bind_result( $intelligence );
if ( $stmt->fetch() ) {
print 'A $animalName has $intelligence Intelligence .n';
} else {
print 'Sorry, no records found.';
}
$stmt->close();
}
$mysqli->close();
?>
実はこの部分このコードは、前に説明したコードの複製です。厳密に手続き型のコードではなく、オブジェクト指向の構文と構成が使用されています。
IV. 高レベルの抽象化
外部ライブラリ PearDB を使用すると、アプリケーションのセキュリティ保護モジュールを完全に抽象化できます。
その一方で、このライブラリの使用には顕著な欠点があります。特定の人々のアイデアによってのみ制限されることがあり、コード管理にも多くの作業が追加されます。このため、適用するかどうかを決定する前に、慎重に検討する必要があります。これを行うことに決めた場合は、少なくともユーザーの入力を「クリーンアップ」するのに実際に役立つことを確認してください。
5. インジェクション保護機能をテストします
前に説明したように、スクリプトの安全性を確保するための重要な部分は、スクリプトをテストすることです。これを行う最善の方法は、SQL コード インジェクション テストを自分で作成することです。
ここでは、そのようなテストの例を示します。この例では、SELECT ステートメントに対するインジェクション攻撃をテストします。
<?php
//テストされた保護関数
functionsafe( $string ) {
return ''' . mysql_real_escape_string( $string ) . ''
}
//データベースに接続します
///// / /////////////////
//注入しようとしています
////////////////////////
$エクスプロイト = 'レミング' AND 1=1;';
//清算
$safe =safe( $exploit );
$query = 'SELECT * FROM 動物 WHERE name = $safe';
$result = mysql_query( $query );
//保護が十分かどうかをテストします
if ( $result && mysql_num_rows( $result ) == 1 ) {
exitt '保護に成功しました:n
exploit $exploit は無効化されました。';
}
else {
exit( '保護に失敗しました:n
exploit $exploit はすべての行を取得できました。' );
}
?>
このようなテスト セットを作成し、インジェクションに基づいてさまざまな SQL コマンドを試したい場合は、次のようにします。保護戦略の欠陥を迅速に検出します。これらのヘッダーを修正すると、インジェクション攻撃に対する真の保護が確立されたと確信できます。
VI. 概要
この一連の記事の冒頭で、SQL インジェクションの議論を通じて、不適切なユーザー入力によって引き起こされる、スクリプトに対する特定の脅威を分析しました。その後、SQL インジェクションがどのように機能するかを説明し、PHP がどのように簡単にインジェクションされるかを正確に分析しました。次に、実際の注入の例を示します。その後、試みられたインジェクション攻撃を無害にするための一連の措置を推奨します。これは、送信されたすべての値が引用符で囲まれていることを確認し、ユーザーが送信した値のタイプを確認し、ユーザー入力をフィルターで除外することによって行われます。危険なキャラクターを待ち伏せするなどの高度な方法によって達成されます。最後に、検証ルーチンを抽象化し、既存のアプリケーションを変更するためのサンプル スクリプトを提供することをお勧めします。次に、サードパーティの抽象化ソリューションの長所と短所について話し合いました。
上記は、PHP における SQL インジェクション攻撃を完全に禁止する 3 番目の内容です。さらに関連する内容については、PHP 中国語 Web サイト (www.php.cn) に注目してください。