今日、 Web アプリの入力検証戦略に関する質問がありました。
執筆時点での一番の答えは、とのみを使用することを示唆してPHP
います。htmlspecialchars
mysql_real_escape_string
私の質問は: これは常に十分ですか? もっと知っておくべきことはありますか?これらの機能はどこで壊れますか?
今日、 Web アプリの入力検証戦略に関する質問がありました。
執筆時点での一番の答えは、とのみを使用することを示唆してPHP
います。htmlspecialchars
mysql_real_escape_string
私の質問は: これは常に十分ですか? もっと知っておくべきことはありますか?これらの機能はどこで壊れますか?
データベース クエリに関しては、常に準備済みのパラメーター化されたクエリを使用してみてください。mysqli
およびPDO
ライブラリはこれをサポートします。これは、 などのエスケープ関数を使用するよりもはるかに安全ですmysql_real_escape_string
。
はい、mysql_real_escape_string
事実上単なる文字列エスケープ関数です。魔法の弾丸ではありません。単一のクエリ文字列で安全に使用できるように、危険な文字をエスケープするだけです。ただし、事前に入力をサニタイズしないと、特定の攻撃ベクトルに対して脆弱になります。
次の 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 では、一連の関数を広く使用して、入力が整数、浮動小数点数、英数字などであることを確認する必要があります。ただし、SQL に関しては、準備されたステートメントの値に最も注意してください。上記のコードは、データベース関数が1 OR 1=1
有効なリテラルではないことを認識しているため、準備されたステートメントであれば安全でした。
についてはhtmlspecialchars()
。それはそれ自身の地雷原です。
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');
これでも、IE6 は UTF を処理する方法が原因で、脆弱なままになります。ただし、IE6 の使用が減少するまでは、ISO-8859-1 などのより制限されたエンコーディングにフォールバックできます。
マルチバイトの問題に関するより詳細な調査については、https://stackoverflow.com/a/12118602/1820を参照してください。
Cheekysoftの優れた回答に加えて:
HTML インジェクション (クロス サイト スクリプティングなど) を防ぐための特効薬はありませんが、HTML を出力するためのライブラリまたはテンプレート システムを使用している場合は、より簡単に達成できる可能性があります。物事を適切にエスケープする方法については、ドキュメントをお読みください。
HTML では、コンテキストに応じて異なる方法でエスケープする必要があります。これは、Javascript に配置される文字列に特に当てはまります。
上記の投稿には間違いなく同意しますが、Cheekysoftの回答に返信して追加する小さなことが1つあります。具体的には次のとおりです。
データベース クエリに関しては、常に準備済みのパラメーター化されたクエリを使用してみてください。mysqli および PDO ライブラリはこれをサポートしています。これは、mysql_real_escape_string などのエスケープ関数を使用するよりもはるかに安全です。
はい、mysql_real_escape_string は事実上単なる文字列エスケープ関数です。魔法の弾丸ではありません。単一のクエリ文字列で安全に使用できるように、危険な文字をエスケープするだけです。ただし、事前に入力をサニタイズしないと、特定の攻撃ベクトルに対して脆弱になります。
次の SQL を想像してください。
$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);
これは悪用に対して脆弱であることがわかるはずです。id パラメーターに一般的な攻撃ベクトルが含まれていると想像してください。
1 または 1=1
エンコードする危険な文字は含まれていないため、エスケープ フィルターをそのまま通過します。私たちを去る:
SELECT フィールド FROM テーブル WHERE id = 1 OR 1=1
数字ではないものをすべて削除する簡単な関数をデータベース クラスにコード化しました。preg_replace を使用しているので、もう少し最適化された機能がある可能性がありますが、ピンチで動作します...
function Numbers($input) {
$input = preg_replace("/[^0-9]/","", $input);
if($input == '') $input = 0;
return $input;
}
したがって、使用する代わりに
$result = "SELECT fields FROM table WHERE id = ".mysqlrealescapestring("1 OR 1=1");
私は使うだろう
$result = "SELECT fields FROM table WHERE id = ".Numbers("1 OR 1=1");
クエリを安全に実行します
SELECT フィールド FROM テーブル WHERE id = 111
確かに、正しい行が表示されなくなっただけですが、サイトにSQLを挿入しようとしている人にとっては大きな問題ではないと思います;)
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];
うまく機能し、64 ビット システムではさらに効果的です。ただし、多数のアドレス指定に対するシステムの制限に注意してください。ただし、データベース ID の場合、これは 99% の確率でうまく機能します。
値をクリーニングするためにも、単一の関数/メソッドを使用する必要があります。この関数が mysql_real_escape_string() の単なるラッパーであっても。なんで?ある日、データを消去するための好みの方法に対するエクスプロイトが見つかった場合、システム全体を検索して置き換えるのではなく、1 か所で更新するだけで済みます。
このパズルの重要なピースはコンテキストです。クエリですべての引数を引用する場合、誰かが 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 までこれをテストしましたが、整数列に文字列コンテキストを使用しても問題は発生しません。