ホームページ  >  記事  >  バックエンド開発  >  PHP 開発で違反できないセキュリティ ルール: ユーザー入力のフィルタリング_PHP チュートリアル

PHP 開発で違反できないセキュリティ ルール: ユーザー入力のフィルタリング_PHP チュートリアル

WBOY
WBOYオリジナル
2016-07-21 15:30:28861ブラウズ

最も基本的な予防策として、外部からの送信に注意を払い、ファイアウォールに対処するための最初のセキュリティ メカニズムを作成する必要があります。
ルール 1: 外部データや入力を決して信頼しない
Web アプリケーションのセキュリティについて最初に認識しなければならないことは、外部データを信頼すべきではないということです。外部データには、プログラマが PHP コードに直接入力しないデータが含まれます。他のソースからのデータ (GET 変数、フォーム POST、データベース、構成ファイル、セッション変数、Cookie など) は、セキュリティを確保するための手順が講じられるまで信頼できません。
たとえば、以下のデータ要素は PHP で設定されているため安全であると考えられます。

コードをコピーします コードは次のとおりです:
$myUsername = 'tmyer'
$arrayUsers = array('tmyer', 'tom', 'tommy'); ("GREETING ", 'hello there' . $myUsername); ただし、次のデータ要素にはすべて欠陥があります。

リスト 2. 安全でない欠陥のあるコード



コードをコピーします
コードは次のとおりです。
$myUsername = $_POST['username'] //tainted! = array($myUsername, 'tom', 'tommy'); // 汚染されています!
汚染されています!変数 $myUsername に欠陥がありますか?これはフォーム POST から直接取得されるためです。ユーザーはこの入力フィールドに任意の文字列を入力できます。これには、ファイルを駆除したり、以前にアップロードしたファイルを実行したりする悪意のあるコマンドも含まれます。 「A ~ Z の文字のみを受け入れるクライアント側 (Javascript) フォーム検証スクリプトを使用すれば、この危険を回避できないのですか?」と疑問に思われるかもしれません。はい、これは常に有益なステップですが、後で説明するように、誰でも任意のフォームを自分のマシンにダウンロードして変更し、必要なものを再送信できます。
解決策は簡単です。サニタイズコードは $_POST['username'] で実行する必要があります。これを行わないと、(配列や定数などで) $myUsername を使用するたびに、これらのオブジェクトが汚染される危険があります。
ユーザー入力をサニタイズする簡単な方法は、正規表現を使用して処理することです。この例では、文字のみが受け入れられることが想定されています。文字列を特定の文字数に制限したり、すべての文字を小文字にすることを要求したりすることも良いアイデアかもしれません。

リスト 3. ユーザー入力を安全にする



コードをコピーします

コードは次のとおりです。
$myUsername = cleanInput($_POST['username']) //clean ! $arrayUsers = array($myUsername, 'tom', 'tommy'); define("GREETING", 'こんにちは' . $myUsername); { $clean = strto lower($input); $clean = preg_replace("/[^a-z]/", "", $clean);
$clean = substr($clean,0,12); clean;
}
?>



ルール 2: セキュリティの実装を困難にする PHP 設定を無効にする

ユーザー入力を信頼できないことは、PHP の仕組みを信頼すべきではないことも知っておく必要があります。マシン上で設定されています。たとえば、register_globals が無効になっていることを確認してください。 register_globals が有効な場合、$variable を使用して GET または POST 文字列を同じ名前に置き換えるなどの不注意な行為が可能になります。この設定を無効にすると、PHP は正しい名前空間の正しい変数を参照するように強制します。フォーム POST から変数を使用するには、$_POST['variable'] を引用符で囲む必要があります。こうすることで、この特定の変数を Cookie、セッション、または GET 変数と間違えることがなくなります。

ルール 3: 理解できない場合、保護することはできません

一部の開発者は、奇妙な構文を使用したり、ステートメントを非常に厳密に編成して、短くても曖昧なコードを形成します。このアプローチは効率的かもしれませんが、コードが何をしているのかを理解していないと、コードを保護する方法を決定できません。
たとえば、以下の 2 つのコードのうちどれが好きですか?

リスト 4. コードを保護しやすくする

コードをコピーする
コードは次のとおりです:

//難読化されたコード $input = (isset($_POST['ユーザー名']) ? $_POST['ユーザー名']:”);
//難読化されていないコード $input = ”; if ($_POST['ユーザー名']){ $input = $_POST['ユーザー名']; }else{
}

;
2 番目のクリーナー スニペットでは、$input に欠陥があり、安全に処理する前にクリーンアップする必要があることが簡単にわかります。
ルール 4: 「多層防御」は新しい魔法です
このチュートリアルでは、フォームを処理する PHP コードで必要な措置を講じながら、オンライン フォームを保護する方法を例を使用して説明します。同様に、PHP 正規表現を使用して GET 変数が完全に数値であることを確認した場合でも、SQL クエリでエスケープされたユーザー入力が使用されることを確認する手順を実行できます。
多層防御は良いアイデアであるだけでなく、深刻な問題に巻き込まれないようにします。
基本的なルールについて説明したので、最初の脅威である SQL インジェクション攻撃を見てみましょう。
SQL インジェクション攻撃を防ぐ
SQL インジェクション攻撃では、ユーザーはフォームまたは GET クエリ文字列を操作することによってデータベース クエリに情報を追加します。たとえば、単純なログイン データベースがあるとします。このデータベースの各レコードには、ユーザー名フィールドとパスワード フィールドがあります。ユーザーがログインできるようにするログイン フォームを作成します。
リスト 5. 単純なログイン フォーム
コードをコピーします コードは次のとおりです。


Login / head>

='テキスト ' 名前='ユーザー' id='ユーザー'/>

<ラベル='pw'>パスワードパスワード' 名前 ='pw' id='pw'/>

<入力タイプ='ログイン'/>

/form>



このフォームは、ユーザーが入力したユーザー名とパスワードを受け入れ、verify.php というファイルに送信します。このファイルでは、PHP は以下に示すようにログイン フォームからのデータを処理します。

リスト 6. 安全でない PHP フォーム処理コード




コードをコピー
コードは次のとおりです:

$ ok = 0;
$username = $_POST['user']; $pw = $_POST['pw']; $sql = “username=' のユーザーから count(*) を選択します。ユーザー名 ."' およびパスワード = '"。$pw."' 制限 1"; $result = mysql_fetch_object($result); ctr == 1){ //アプリケーションに入っても大丈夫です!
$okay = 1;
}
if ($okay){
$_SESSION['loginokay'] = true; Index .php");
}else{
header("login.php");
}
?>


このコードは問題ありませんね?このようなコードは、世界中の数百 (数千ではないにしても) の PHP/MySQL サイトで使用されています。どうしたの? 「ユーザー入力は信頼できない」ということを覚えておいてください。ここではユーザーからの情報はエスケープされないため、アプリケーションは脆弱なままになります。具体的には、あらゆる種類の SQL インジェクション攻撃が可能です。
たとえば、ユーザーがユーザー名として foo を入力し、パスワードとして ' または '1'='1 を入力すると、実際には次の文字列が PHP に渡され、クエリが MySQL に渡されます:



コードをコピー

コードは次のとおりです。


$sql = “select count(*) as ctr from users where username='foo' and password=” or '1'='1' limit 1''; ?>

このクエリは常に 1 を返すため、PHP はパスワード文字列の末尾に悪意のある SQL を挿入することでアクセスを許可します。
これを解決する方法。問題は、PHP の組み込み mysql_real_escape_string() 関数が、文字列内の文字をエスケープし、アポストロフィなどの特殊文字を渡すことを不可能にし、MySQL がそれらの文字を操作できるようにすることです。エスケープあり
リスト 7. 安全な PHP フォーム処理コード
コードをコピーします

コードは次のとおりです:

$okay = 0;
$username = $_POST['user'];
$pw = $_POST['pw'];
$sql = “ユーザーからのctrとして選択しますここで、ユーザー名 = '".mysql_real_escape_string($username)."' およびパスワード ='".mysql_real_escape_string($pw)."' 制限 1'';
$result = mysql_query($sql)
while ($data = mysql_fetch_object( $result)){
if ($data->ctr == 1){
//アプリケーションに入っても大丈夫です
}
}
if ($okay){
$ _SESSION['loginokay'] = true;
}else{
header("login.php");
?>
ユーザー入力ラッパーとして使用しますを使用すると、ユーザー入力における悪意のある SQL インジェクションを回避できます。ユーザーが SQL インジェクションを介して不正なパスワードを渡そうとすると、次のクエリがデータベースに渡されます:
select count(*) as ctr from users where username='foo' and password='' or '1'=' 1' 制限 1''
そのようなパスワードに一致するものはデータベースにはありません。たった 1 つの簡単な手順で、Web アプリケーションの大きな穴が塞がれてしまいました。ただし、ここでの教訓は、SQL クエリに対するユーザー入力は常にエスケープである必要があるということです。ブロックする必要があるセキュリティ ホールがいくつかあります。次の項目は GET 変数の操作です。
ユーザーによる GET 変数の操作の防止
前のセクションでは、ユーザーが不正なパスワードでログインすることを防止しました。 SQL ステートメントへのすべてのユーザー入力が確実にエスケープされるようにする方法を学習しました
ただし、ユーザーが安全にログインしたからといって、ユーザーがルールに従って行動することを意味するわけではありません。 — たとえば、損害を引き起こす可能性が数多くあります。 、アプリケーションでは、ユーザーが特別なコンテンツを表示できるようにする場合があります。template.php?pid=321 の疑問符の後の場所を指すリンクは、クエリ文字列が URL に直接配置されるため、このように呼ばれることもあります。 GET クエリ文字列。PHP では、register_globals が無効になっている場合、$_GET['pid'] を使用して、template.php ページでこの文字列にアクセスできます。

リスト 8。 template.php の例




コードをコピーします
コードは次のとおりです:
$pid = $_GET ['pid'] //架空のクラスのオブジェクトを作成します。 Page $obj = new Page; $content = $obj->fetchPage($pid);
?> を表示する PHP ができました。何よりも、ブラウザからの GET 変数 pid を暗黙的に信頼します。ただし、ほとんどのユーザーがブラウザの URL 位置フィールドにある pid=33 に気づいた場合、セマンティック攻撃を構築する可能性があります。別の番号を入力する場合は問題ないかもしれませんが、SQL コマンドやファイル名 (/etc/passwd など) を入力するなど、他のいたずらをした場合は問題ありません。最大 3,000 文字の値を指定するとどうなりますか?
この場合、ユーザー入力を信用しないという基本的なルールを思い出してください。アプリケーション開発者は、template.php で受け入れられる個人識別子 (PID) が数値である必要があることを知っているため、以下に示すように、PHP の is_numeric() 関数を使用して、数値以外の PID が受け入れられないようにすることができます。 ) GET 変数を制限するには




コードをコピーします

コードは次のとおりです:

$pid = $_GET['pid']
if (is_numeric($pid)){ //架空のクラス Page のオブジェクトを作成します
$obj = new Page;
$content = $obj->fetchPage($pid); これで、ページを表示する一連の PHP が完成しました
}else{ / /is_numeric() テストに合格しませんでした。他のことをしてください! } ?>
このメソッドは有効のようですが、次の入力は is_numeric() チェックに簡単に合格できます。 100 (有効)
100.1 (小数点以下の桁を含めないでください)
+0123.45e6 (科学表記 - 悪い)
0xff33669f (16 進数 - 危険! 危険!)
それでは、セキュリティを重視する PHP 開発者は何をすべきでしょうか?長年の経験から、以下に示すように、正規表現を使用して GET 変数全体が数値で構成されていることを確認するのがベスト プラクティスであることがわかっています。
コードは次のとおりです:

$pid = $_GET['pid'];
if (strlen($pid)){
if (!ereg("^[0-9]+$",$pid)){
//ログアウトするか、ホームページに送り返すなど、適切な操作を行ってください
}
}else{
//$pid が空なので、ホームページに送り返します
}
// のオブジェクトを作成します架空のクラス Page
//悪意のあるユーザー入力から適度に保護されています
$obj = new Page;
$content = $obj->fetchPage($pid); そして、これで大量の PHP が完成しました。ページ
?>

を表示します。必要なのは、strlen() を使用して変数の長さがゼロでないことを確認することだけです。そうであれば、データ要素がゼロでないことを確認します。有効です。 PID に文字、スラッシュ、ピリオド、または 16 進数に類似したものが含まれている場合、このルーチンはそれを捕捉し、ユーザーのアクティビティからページをブロックします。 Page クラスの舞台裏を見ると、以下に示すように、セキュリティを意識した PHP 開発者がユーザー入力 $pid をエスケープし、それによって fetchPage() メソッドを保護していることがわかります。脱出するには



コードをコピーします
コードは次のとおりです: class Page{
function fetchPage($pid){
$sql = “select pid,title,desc,kw ,content,status from page where pid='".mysql_real_escape_string($pid)."'";
}
}
?>


「PID が数値であることは確認しているのに、なぜわざわざ? エスケープ?」 fetchPage() メソッドがどれだけ多くの異なるコンテキストや状況で使用されるかわからないからです。このメソッドが呼び出されるすべての場所で保護を提供する必要があり、メソッド内でのエスケープは多層防御の意味を具体化します。
ユーザーが非常に長い値 (最大 1000 文字など) を入力してバッファ オーバーフロー攻撃を開始しようとした場合はどうなりますか?次のセクションでこれについて詳しく説明しますが、ここでは別のチェックを追加して、入力 PID が正しい長さであることを確認できます。データベースの pid フィールドの最大長は 5 桁であることがわかっているため、次のチェックを追加できます。
リスト 12. 正規表現と長さチェックを使用して GET 変数を制限する



コードをコピーする
コードは次のとおりです。 if ( strlen($pid)){ if (!ereg("^[0-9]+$",$pid) && strlen($pid) > 5){ //ログ記録など、適切な処理を行いますそれらを送信するか、ホームページに送り返します } } else {
//$pid が空なので、ホームページに送り返します
}
//架空のクラス Page のオブジェクトを作成します。これは現在
/ /悪意のあるユーザー入力からさらに保護されます
$obj = new Page;
$content = $obj->fetchPage($pid);
?>

現在、データベース アプリケーションに 5,000 桁の値を詰め込むことは誰にもできません (少なくとも GET 文字列が関係する場合はそうではありません)。ハッカーがアプリケーションに侵入しようとしてイライラして歯ぎしりしているところを想像してみてください。また、エラー報告がオフになっているため、ハッカーによる偵察が困難になります。
バッファ オーバーフロー攻撃
バッファ オーバーフロー攻撃 PHP アプリケーション (より正確には、Apache または基盤となるオペレーティング システム) のメモリ割り当てバッファをオーバーフローさせようとする試み。 Web アプリケーションを PHP などの高級言語で作成している可能性がありますが、最終的には C を呼び出していることに注意してください (Apache の場合)。ほとんどの低レベル言語と同様、C にはメモリ割り当てに関する厳密なルールがあります。
バッファ オーバーフロー攻撃は、大量のデータをバッファに送信し、データの一部を隣接するメモリ バッファにオーバーフローさせ、バッファを破壊したりロジックを書き換えたりします。これにより、サービス拒否、データの破損、またはリモート サーバー上で悪意のあるコードの実行が発生する可能性があります。
バッファ オーバーフロー攻撃を防ぐ唯一の方法は、すべてのユーザー入力の長さをチェックすることです。たとえば、ユーザー名を要求するフォーム要素がある場合、このフィールドに値 40 の maxlength 属性を追加し、バックエンドで substr() を使用して確認します。リスト 13 に、フォームと PHP コードの簡単な例を示します。

リスト 13. ユーザー入力の長さを確認します



コードをコピーします

コードは次のとおりです:

if ($_POST['submit'] == "go"){
$name = substr($_POST['name'],0,40)
}
< ;form action=”” method=”post”>






なぜ maxlength 属性を提供するだけでなく、バ​​ックエンドで substr() チェックも実行するのでしょうか?多層防御は常に有効だからです。ブラウザーは、PHP や MySQL が安全に処理できない非常に長い文字列 (誰かが最大 1,000 文字の名前を入力しようとしているところを想像してください) をユーザーが入力することを防ぎ、バックエンド PHP チェックにより、誰もフォーム データをリモートまたはブラウザーで操作していないことを確認します。 。
ご覧のとおり、このアプローチは、前のセクションで strlen() を使用して GET 変数 pid の長さをチェックするのと似ています。この例では、5 桁を超える入力値は無視されますが、以下に示すように、値は適切な長さに簡単に切り詰められます。

リスト 14. 入力 GET 変数の長さの変更


コード
コードは次のとおりです: $pid = $_GET['pid'];
if (strlen($pid)){
if (!ereg(”^[0-9 ] +$”,$pid)){
//数値でない $pid の場合、ホームページに送り返す
}
}else{
//$pid が空なので、ホームページに送り返す
}
// 数値の PID がありますが、長すぎる可能性があるので確認しましょう
if (strlen($pid)>5){
$pid = substr($pid,0,5)
}
//架空のクラス Page のオブジェクトを作成します。これで
//悪意のあるユーザー入力からさらに保護されます
$obj = new Page;
//そして今ページ
?> を表示する大量の PHP があります


バッファ オーバーフロー攻撃は数字や文字の長い文字列に限定されないことに注意してください。また、長い 16 進文字列 (xA3 や xFF のように見える) が表示されることもあります。バッファ オーバーフロー攻撃の目的は、特定のバッファをフラッドし、悪意のあるコードや命令を次のバッファに配置し、それによってデータを破損したり、悪意のあるコードを実行したりすることであることに注意してください。 16 進バッファ オーバーフローに対処する最も簡単な方法は、入力が特定の長さを超えないようにすることです。
データベース内で長いエントリを許可するフォームのテキスト領域を扱っている場合、クライアント側でデータの長さを簡単に制限する方法はありません。データが PHP に到達したら、正規表現を使用して 16 進数のような文字列を消去できます。
リスト 15. 16 進文字列の防止



コードをコピー
コードは次のとおりです: if ($_POST['submit'] == "go"){
$ name = substr($_POST['name'],0,40);
//潜在的な 16 進文字を削除します
$name = cleanHex($name);
}
function cleanHex($ input) ){
$clean = preg_replace("![][xX]([A-Fa-f0-9]{1,3})!", "",$input);
return $clean
}
? >
” method=”post”>




この一連の操作は少し厳しすぎると感じるかもしれません。結局のところ、16 進文字列には、外国語で文字を印刷するなど、正当な用途があります。 16 進正規表現をどのように展開するかはあなた次第です。より良い戦略は、1 行に 16 進文字列が多すぎる場合、または文字列が特定の文字数 (128 や 255 など) を超える場合にのみ、16 進文字列を削除することです。
クロスサイト スクリプティング攻撃
クロスサイト スクリプティング (XSS) 攻撃では、多くの場合、悪意のあるユーザーが (または他のユーザー入力方法を通じて) フォームに情報を入力し、悪意のあるクライアント側のタグがプロセスまたはデータベースに挿入されます。 。たとえば、サイトに訪問者が名前、電子メール アドレス、簡単なメッセージを残せる簡単なゲストブック プログラムがあるとします。悪意のあるユーザーはこの機会を利用して、他のユーザーにとって不適切な画像やユーザーを別のサイトにリダイレクトする Javascript など、短いメッセージ以外の内容を挿入したり、Cookie 情報を盗んだりする可能性があります。
幸いなことに、PHP には、HTML タグで囲まれたコンテンツを削除できるstrip_tags() 関数が用意されています。また、strip_tags() 関数を使用すると、 など、許可されるタグのリストを提供できます。
ブラウザ内データ操作
ユーザーがページ上のヘッダー要素やフォーム要素を改ざんできるようにするブラウザー プラグインの一種があります。 Mozilla プラグインである Tamper Data を使用すると、多くの非表示のテキスト フィールドを持つ単純なフォームを簡単に操作して、PHP や MySQL に指示を送信できます。
ユーザーがフォームで「送信」をクリックする前に、データ改ざんを開始できます。フォームを送信すると、フォーム データ フィールドのリストが表示されます。データの改ざんを使用すると、ブラウザーがフォームの送信を完了する前に、ユーザーがこのデータを改ざんできます。
前に作成した例に戻りましょう。文字列の長さがチェックされ、HTML タグが削除され、16 進文字が削除されました。ただし、以下に示すように、いくつかの隠しテキスト フィールドが追加されます。
リスト 17. 隠し変数
コードをコピー コードは次のとおりです。

if ($_POST['submit '] == "go"){
//strip_tags
$name =trip_tags($_POST['name'])
$name = substr($name,0,40);
//潜在的な 16 進数を削除します。文字
$name = cleanHex($name);
//処理を続行します…
}
function cleanHex($input){
$clean = preg_replace(”![][xX]([A-Fa-f0-9 ]{ 1,3})!", "",$input);
return $clean;
}
?>








隠し変数の 1 つがテーブル名 users を公開していることに注意してください。また、create という値を持つアクション フィールドも表示されます。基本的な SQL の経験がある人なら、これらのコマンドがおそらくミドルウェアの SQL エンジンを制御していることがわかります。大混乱を引き起こしたい人は、テーブル名を変更するか、削除などの別のオプションを提供するだけで済みます。
今残っている質問は何ですか?リモートフォーム送信。
リモートフォーム送信
Webの利点は、情報やサービスを共有できることです。欠点は、情報やサービスの共有です。何の躊躇もなく物事を行う人もいます。
フォームを例に挙げてみましょう。誰でも Web サイトにアクセスし、ブラウザ上で [ファイル] > [名前を付けて保存] を使用してフォームのローカル コピーを作成できます。次に、完全修飾 URL (formHandler.php ではなく、フォームはこのサイトにあるため http://www.yoursite.com/formHandler.php) を指すようにアクション パラメーターを変更し、必要な操作を行うことができます。変更を加えて [送信] をクリックすると、サーバーはこのフォーム データを法的な通信フローとして受信します。
まず、$_SERVER['HTTP_REFERER'] をチェックして、リクエストが自分のサーバーからのものであるかどうかを判断することを検討してください。この方法では、ほとんどの悪意のあるユーザーをブロックできますが、最も高度なハッカーをブロックすることはできません。これらの人々は、ヘッダー内の参照元情報を改ざんして、フォームのリモート コピーをサーバーから送信されたように見せかけるほど賢いのです。
リモート フォームの送信を処理するより良い方法は、一意の文字列またはタイムスタンプに基づいてトークンを生成し、このトークンをセッション変数とフォームに入れることです。フォームを送信した後、2 つのトークンが一致するかどうかを確認します。一致しない場合は、誰かがフォームのリモート コピーからデータを送信しようとしていることがわかります。
ランダム トークンを作成するには、以下に示すように、PHP の組み込み md5()、uniqid()、および rand() 関数を使用できます:
リスト 18. リモート フォーム送信に対する防御
コードをコピーします コードは次のとおりです:

session_start();
if ($_POST['submit'] == "go"){
//チェックトークン
if ($_POST['token' ] == $_SESSION[ 'token']){
//strip_tags
$name =trip_tags($_POST['name']);
$name = substr($name,0,40);潜在的な 16 進文字
$ name = cleanHex($name);
//処理を続行します...
}else{
//すべての処理を停止します!
}
}
$token(uniqid(rand) ()、true)) ;
$_SESSION['token']= $token;
function cleanHex($input){
$clean = preg_replace("![][xX]([A-Fa-f0-9] {1,3}) !", "",$input);
return $clean;
}
?>
” method=”post” >



”/> p>





この手法は、PHP セッションではデータを保存できないため有効です。サーバー間の移行。誰かがあなたの PHP ソース コードを取得し、それを自分のサーバーに移動し、あなたのサーバーに情報を送信したとしても、あなたのサーバーが受け取るのは、空の、または不正な形式のセッション トークンと、最初に提供されたフォーム トークンだけです。これらは一致しないため、リモート フォームの送信は失敗します。

http://www.bkjia.com/PHPjc/323236.html

tru​​ehttp://www.bkjia.com/PHPjc/323236.html技術記事最も基本的な予防策として、外部からの送信に注意を払い、ファイアウォールに対処するための最初のセキュリティ メカニズムを作成する必要があります。 ルール 1: 外部データや入力を決して信用しない Web アプリケーションのセキュリティに関しては...
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。