web-dev-qa-db-ja.com

htmlspecialcharsとmysql_real_escape_stringはPHPコードをインジェクションから安全に保ちますか?

今日、 Webアプリの入力検証戦略 に関して質問がありました。

執筆時点での一番の答えは、PHPhtmlspecialcharsmysql_real_escape_string

私の質問は、これで常に十分ですか?もっと知っておくべきことはありますか?これらの機能はどこで故障しますか?

115
Cheekysoft

データベースクエリに関しては、常に準備されたパラメータ化されたクエリを使用してみてください。 mysqliおよびPDOライブラリーはこれをサポートします。これは、_mysql_real_escape_string_などのエスケープ関数を使用するよりもはるかに安全です。

はい、_mysql_real_escape_string_は事実上単なる文字列エスケープ関数です。これは魔法の弾丸ではありません。 1つのクエリ文字列で安全に使用できるように、危険な文字をエスケープするだけです。ただし、事前に入力をサニタイズしないと、特定の攻撃ベクトルに対して脆弱になります。

次のSQLを想像してください。

_$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);
_

これがエクスプロイトに対して脆弱であることを確認できるはずです。
idパラメーターに共通の攻撃ベクトルが含まれていると想像してください。

_1 OR 1=1
_

エンコードする危険な文字はありませんので、エスケープフィルターを直接通過します。立ち去る:

_SELECT fields FROM table WHERE id= 1 OR 1=1
_

これは素敵なSQLインジェクションベクターであり、攻撃者はすべての行を返すことができます。または

_1 or is_admin=1 order by id limit 1
_

生産する

_SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1
_

この完全に架空の例では、攻撃者は最初の管理者の詳細を返すことができます。

これらの関数は便利ですが、注意して使用する必要があります。すべてのWeb入力がある程度検証されるようにする必要があります。この場合、数値として使用していた変数が実際に数値であることを確認しなかったため、悪用される可能性があります。 PHPでは、入力が整数、浮動小数点数、英数字などであることをチェックするために一連の関数を広く使用する必要があります。データベース関数が_1 OR 1=1_が有効なリテラルではないことを知っていたため、準備されたステートメントであれば安全でした。

htmlspecialchars()に関しては。それはそれ自身の地雷原です。

PHPには実際の問題があります。PHP$ ===には、さまざまなhtml関連のエスケープ関数の選択があり、どの関数が何をするかについての明確なガイダンスがありません。

まず、HTMLタグ内にいる場合、実際に問題が発生します。見る

_echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';
_

すでにHTMLタグの内側にいるので、危険なことをするために<や>は必要ありません。攻撃ベクトルはjavascript:alert(document.cookie)になります

結果のHTMLは次のようになります

_<img src= "javascript:alert(document.cookie)" />
_

攻撃は直進します。

悪化する。どうして? htmlspecialchars(この方法で呼び出されたとき)は、単一ではなく二重引用符のみをエンコードするためです。だから我々が持っていた場合

_echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";
_

邪悪な攻撃者は、まったく新しいパラメーターを注入できるようになりました

_pic.png' onclick='location.href=xxx' onmouseover='...
_

私たちに与えます

_<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />
_

これらの場合、特効薬はありません。入力を自分で正当化するだけです。不良文字を除外しようとすると、必ず失敗します。ホワイトリストのアプローチを採用し、良い文字のみを許可します。 XSSチートシート をご覧ください

HTMLタグの外でhtmlspecialchars($string)を使用しても、マルチバイト文字セット攻撃ベクトルに対して脆弱です。

最も効果的な方法は、mb_convert_encodingとhtmlentitiesの組み合わせを次のように使用することです。

_$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');
_

これでも、UTFの処理方法のため、IE6は脆弱です。ただし、IE6の使用が途切れるまで、ISO-8859-1などのより制限されたエンコードにフォールバックできます。

マルチバイトの問題に関するより詳細な調査については、 https://stackoverflow.com/a/12118602/182 を参照してください

239
Cheekysoft

Cheekysoftの優れた答えに加えて:

  • はい、彼らはあなたを安全に保ちますが、それらが絶対に正しく使用されている場合のみです。それらを誤って使用すると、依然として脆弱であり、他の問題(データ破損など)が発生する可能性があります
  • 代わりにパラメーター化されたクエリを使用してください(上記のとおり)。あなたはそれらを使用することができますPDOまたはPEAR DBのようなラッパー経由
  • Magic_quotes_gpcとmagic_quotes_runtimeが常にオフになっていることを確認してください。これらは、セキュリティの問題(データを破壊する)を防ぐためのPHPの開発者による初期の深く誤った試みです。

HTMLインジェクション(クロスサイトスクリプティングなど)を防止するための特効薬はありませんが、HTMLの出力にライブラリまたはテンプレートシステムを使用している場合は、より簡単に達成できる可能性があります。物事を適切にエスケープする方法については、そのためのドキュメントをお読みください。

HTMLでは、コンテキストに応じて異なる方法でエスケープする必要があります。これは、Javascriptに配置される文字列の場合に特に当てはまります。

10
MarkR

上記の投稿には間違いなく同意しますが、Cheekysoftの回答への返信として、具体的には次の小さなことを追加します。

データベースクエリに関しては、常に準備されたパラメータ化されたクエリを使用してみてください。 mysqliおよびPDOライブラリはこれをサポートしています。これは、mysql_real_escape_stringなどのエスケープ関数を使用するよりもはるかに安全です。

はい、mysql_real_escape_stringは事実上、単なる文字列エスケープ関数です。これは魔法の弾丸ではありません。 1つのクエリ文字列で安全に使用できるように、危険な文字をエスケープするだけです。ただし、事前に入力をサニタイズしないと、特定の攻撃ベクトルに対して脆弱になります。

次のSQLを想像してください。

$ result = "SELECTフィールドFROMテーブルWHERE id =" .mysql_real_escape_string($ _ POST ['id']);

これがエクスプロイトに対して脆弱であることを確認できるはずです。 idパラメータに共通の攻撃ベクトルが含まれていると想像してください。

1 OR 1 = 1

エンコードする危険な文字はありませんので、エスケープフィルターを直接通過します。立ち去る:

テーブルからフィールドを選択WHERE id = 1 OR 1 = 1

私は、データベースクラスに入れた簡単な小さな関数をコーディングしました。 preg_replaceを使用するため、probにはもう少し最適化された関数がありますが、ピンチで動作します...

function Numbers($input) {
  $input = preg_replace("/[^0-9]/","", $input);
  if($input == '') $input = 0;
  return $input;
}

そのため、代わりに

$ result = "SELECTフィールドFROMテーブルWHERE id =" .mysqlrealescapestring( "1 OR 1 = 1");

私は使うだろう

$ result = "SELECTフィールドFROMテーブルWHERE id =" .Numbers( "1 OR 1 = 1");

クエリを安全に実行します

テーブルからフィールドを選択WHERE id = 111

確かに、正しい行が表示されなくなっただけですが、それをあなたのサイトにsqlを挿入しようとしている人にとって大きな問題だとは思いません;)

3
BrilliantWinter

このパズルの重要な部分はコンテキストです。クエリのすべての引数を引用する場合、IDとして「1 OR 1 = 1」を送信する人は問題ではありません。

SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'"

結果:

SELECT fields FROM table WHERE id='1 OR 1=1'

効果がありません。文字列をエスケープしているため、入力が文字列コンテキストから抜け出すことはできません。 MySQLのバージョン5.0.45でこれをテストしましたが、整数列に文字列コンテキストを使用しても問題は発生しません。

2
Lucas Oman
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];

うまく機能し、64ビットシステムではさらに良くなります。ただし、多数のアドレスを指定する場合のシステムの制限に注意してください。ただし、データベースIDの場合、これは99%の時間でうまく機能します。

値のクリーニングにも単一の関数/メソッドを使用する必要があります。この関数がmysql_real_escape_string()の単なるラッパーであっても。どうして?希望するデータクリーニング方法の悪用が見つかった日は、システム全体の検索と置換ではなく、1か所で更新するだけで済みます。

2
cnizzardini