セキュリティが重要であることは誰もが知っていますが、業界の傾向は最後の瞬間までセキュリティを追加しないことです。 Web アプリケーションを完全に保護することは不可能なので、わざわざ心配する必要はありません。間違っている。いくつかの簡単な手順を実行するだけで、PHP Web アプリケーションのセキュリティを大幅に向上させることができます。
始める前に
このチュートリアルでは、独自の PHP Web アプリケーションにセキュリティを追加する方法を学びます。このチュートリアルでは、PHP Web アプリケーションの作成経験が少なくとも 1 年あることを前提としているため、PHP 言語の基本的な知識 (規則または構文) についてはここでは説明しません。目的は、構築する Web アプリケーションを保護する方法についてのアイデアを提供することです。
目的
このチュートリアルでは、SQL インジェクション、GET および POST 変数の操作、バッファ オーバーフロー攻撃、クロスサイト スクリプティング攻撃、ブラウザ内データ操作、リモート フォーム送信などの最も一般的なセキュリティ脅威から保護する方法について説明します。
前提条件
このチュートリアルは、少なくとも 1 年のプログラミング経験を持つ PHP 開発者を対象に書かれています。 PHP の構文と規則については理解する必要がありますが、ここでは説明しません。ここで説明するルールの多くは他の言語や環境にも適用されるため、Ruby、Python、Perl などの他の言語を使用した経験のある開発者もこのチュートリアルから恩恵を受けるでしょう。
システム要件
PHP V4 または V5 と MySQL を実行する環境が必要です。 Linux、OS X、または Microsoft Windows を使用できます。 Windows を使用している場合は、WAMPServer バイナリをダウンロードし、Apache、MySQL、および PHP をマシンにインストールします。
セキュリティの簡単な紹介
Web アプリケーションの最も重要な部分は何ですか?誰が質問に答えるかによって、この質問に対する答えは異なる場合があります。ビジネスマンは信頼性と拡張性を必要としています。 IT サポート チームは、堅牢で保守可能なコードを必要としています。エンドユーザーは、タスクを実行する際に美しいユーザー インターフェイスと高いパフォーマンスを必要とします。ただし、答えが「セキュリティ」であれば、これが Web アプリケーションにとって重要であることに誰もが同意するでしょう。
しかし、ほとんどの議論はここで終わります。セキュリティはプロジェクトのチェックリストに含まれていますが、多くの場合、プロジェクトが納品されるまで対処されません。このアプローチを採用している Web アプリケーション プロジェクトの数は驚異的です。開発者は何か月もかけて取り組み、最後にセキュリティ機能を追加して、Web アプリケーションを一般公開できるようにしました。
セキュリティ機能が追加される前にコードが検査され、単体テストされ、より大きなフレームワークに統合されているため、結果は混乱したり、再作業が必要になったりすることがよくあります。セキュリティを追加すると、主要なコンポーネントが動作しなくなる場合があります。セキュリティを統合すると、スムーズな (ただし安全ではない) プロセスに余分な負担やステップが追加されます。
このチュートリアルは、PHP Web アプリケーションにセキュリティを統合するための優れた方法を提供します。いくつかの一般的なセキュリティのトピックについて説明し、その後、主要なセキュリティの脆弱性とその解決方法について詳しく説明します。このチュートリアルを完了すると、セキュリティについてより深く理解できるようになります。
トピックには以下が含まれます:
SQL インジェクション攻撃
GET 文字列の操作
バッファ オーバーフロー攻撃
クロスサイト スクリプティング攻撃 (XSS)
ブラウザ内データ操作
リモート フォーム送信
Web セキュリティ 101
セキュリティ実装の詳細について事前に話し合うことが最善ですWeb アプリケーションのセキュリティを高レベルの観点から説明します。このセクションでは、作成する Web アプリケーションの種類に関係なく、心に留めておくべきセキュリティ哲学の基本原則をいくつか紹介します。これらのアイデアの中には、Chris Shiflett (PHP セキュリティーに関する著書は貴重な宝庫です) から得たもの、Simson Garfinkel (「参考文献」を参照) から得たもの、そして長年蓄積された知識から得たものもあります。
ルール 1: 外部データや入力を決して信頼しない
Web アプリケーションのセキュリティについて最初に認識しなければならないことは、外部データは信頼すべきではないということです。外部データには、プログラマが PHP コードに直接入力しないデータが含まれます。他のソースからのデータ (GET 変数、フォーム POST、データベース、構成ファイル、セッション変数、Cookie など) は、セキュリティを確保するための手順が講じられるまで信頼できません。
たとえば、次のデータ要素は PHP で設定されているため安全であると考えられます。
リスト 1. 安全で完璧なコード
$myUsername = 'tmyer';
$arrayUsers = array('tmyer', 'tom', 'tommy'); ' . $myUsername);
?> ただし、次のデータ要素にはすべて欠陥があります。リスト 2. 安全でない欠陥のあるコード
$myUsername = $_POST['username'] //汚染されています!
$arrayUsers = array($myUsername, 'tom', 'tommy'); !
define("GREETING", 'hello there' . $myUsername);
?>
最初の変数 $myUsername が間違っているのはなぜですか?これはフォーム POST から直接取得されるためです。ユーザーはこの入力フィールドに任意の文字列を入力できます。これには、ファイルを駆除したり、以前にアップロードしたファイルを実行したりする悪意のあるコマンドも含まれます。 「A ~ Z の文字のみを受け入れるクライアント側 (JavaScript) フォーム検証スクリプトを使用すれば、この危険を回避できないのですか?」と疑問に思われるかもしれません。はい、これは常に有益なステップですが、後で説明するように、誰でもあらゆるフォームを自分のマシンにダウンロードし、変更して、必要なものを再送信できます。
解決策は簡単です。サニタイズコードは $_POST['username'] で実行する必要があります。これを行わないと、(配列や定数などで) $myUsername を使用するたびに、これらのオブジェクトが汚染される可能性があります。
ユーザー入力をサニタイズする簡単な方法は、正規表現を使用して処理することです。この例では、文字のみが受け入れられることが想定されています。文字列を特定の文字数に制限したり、すべての文字を小文字にすることを要求したりすることも良いアイデアかもしれません。
リスト 3. ユーザー入力を安全にする
$myUsername = cleanInput($_POST['username']) //clean!
$arrayUsers = array($myUsername, 'tom', 'tommy' ); //クリーン!
define("GREETING", 'こんにちは' . $myUsername)
function cleanInput($input){
$clean = preg_replace( "/ [^a-z]/", "", $clean);
$clean = substr($clean,0,12);
return $clean;
}
?>
ルール 2: セキュリティを難しくするものを無効にする PHPセットアップ
ユーザー入力が信頼できないことがわかったので、マシン上での PHP の構成方法も信頼すべきではないことも理解する必要があります。たとえば、register_globals が無効になっていることを確認してください。 register_globals が有効な場合、$variable を使用して同じ名前の GET または POST 文字列を置き換えるなどの不注意な行為が可能になります。この設定を無効にすると、PHP は正しい名前空間の正しい変数を参照するように強制します。 POST フォームから変数を使用するには、$_POST['variable'] を引用符で囲む必要があります。こうすることで、この特定の変数を Cookie、セッション、または GET 変数として誤って解釈することがなくなります。
2 番目に確認する設定は、エラー報告レベルです。開発中はできるだけ多くのエラー レポートを取得したいと考えますが、プロジェクトを納品するときは、エラーを画面に表示するのではなくログ ファイルに記録したいと考えます。なぜ?悪意のあるハッカーは、エラー報告情報 (SQL エラーなど) を使用して、アプリケーションが何を行っているかを推測する可能性があるためです。この種の偵察は、ハッカーがアプリケーションに侵入するのに役立つ可能性があります。この脆弱性を解決するには、php.ini ファイルを編集して error_log エントリの適切な保存先を指定し、display_errors を Off に設定します。
ルール 3: 理解できない場合、保護することはできません
一部の開発者は、奇妙な構文を使用したり、ステートメントを非常にコンパクトに編成して、短くても曖昧なコードを形成します。このアプローチは効率的かもしれませんが、コードが何をしているのかを理解していないと、コードを保護する方法を決定できません。
たとえば、以下の 2 つのコードのうちどれが好きですか?
リスト 4. コードを保護しやすくする
//難読化されたコード
$input = (isset($_POST['username']) ? $_POST['username']:''); / 難読化されていないコード
$input = '';
if (isset($_POST['ユーザー名']){
}else{
$input = '';
?>
2 番目の明確なコード スニペットでは、$input に欠陥があり、安全に処理する前にクリーンアップする必要があることが簡単にわかります。
ルール 4: 「多層防御」は新しい魔法の武器です
このチュートリアルでは、フォームを処理する PHP コードで必要な措置を講じながら、オンライン フォームを保護する方法を例を使用して説明します。同様に、PHP 正規表現を使用して GET 変数が完全に数値であることを確認した場合でも、SQL クエリでエスケープされたユーザー入力が使用されることを確認する手順を実行できます。
多層防御は良いアイデアであるだけでなく、深刻な問題に巻き込まれないようにします。
基本的なルールについて説明したので、最初の脅威である SQL インジェクション攻撃を見てみましょう。
SQL インジェクション攻撃を防ぐ
SQL インジェクション攻撃では、ユーザーはフォームまたは GET クエリ文字列を操作することによってデータベース クエリに情報を追加します。たとえば、単純なログイン データベースがあるとします。このデータベースの各レコードには、ユーザー名フィールドとパスワード フィールドがあります。ユーザーがログインできるようにするログイン フォームを作成します。
リスト 5. 単純なログインフォーム
このフォームは受け入れますユーザーからユーザー名とパスワードを入力し、そのユーザー入力を verify.php という名前のファイルに送信します。このファイルでは、PHP は以下のようにログイン フォームからのデータを処理します。
リスト 6. 安全でない PHP フォーム処理コード
$okay = 0;
$username = $_POST['user'] ; pw = $_POST['pw'];
$sql = "ユーザー名 = '".$username."' およびパスワード = '" から count(*) を CTR として選択します。$pw."' 制限 1";
$result = mysql_query($sql);
while ($data = mysql_fetch_object($result)){
if ($data->ctr == 1){
//アプリケーションに入っても大丈夫です
$okay = 1;
}
}
if ($okay){
$_SESSION['loginokay'] = true;
}else{
header("login.php") ;
}
?>
このコードは問題ありませんね?世界中の数百 (数千ではないにしても) の PHP/MySQL サイトがこのようなコードを使用しています。どうしたの? 「ユーザー入力は信頼できない」ということを覚えておいてください。ここではユーザーからの情報はエスケープされないため、アプリケーションは脆弱なままになります。具体的には、あらゆる種類の SQL インジェクション攻撃が発生する可能性があります。
たとえば、ユーザーがユーザー名として foo を入力し、パスワードとして ' または '1'='1 を入力すると、実際には次の文字列が PHP に渡され、クエリが MySQL に渡されます:
$sql = "select count(*) as ctr from users where username='foo' and passwd='' or '1'='1' limit 1"; このクエリは常にカウント値 1 を返します。したがって、PHP アクセスが許可されます。ハッカーは、パスワード文字列の末尾に悪意のある SQL を挿入することで、正規のユーザーになりすますことができます。
この問題の解決策は、PHP の組み込み mysql_real_escape_string() 関数をユーザー入力のラッパーとして使用することです。この関数は文字列内の文字をエスケープするため、文字列内でアポストロフィなどの特殊文字を渡すことができなくなり、MySQL が特殊文字を操作できるようになります。リスト 7 はエスケープされたコードを示しています。
リスト 7. 安全な PHP フォーム処理コード
$okay = 0;
$username = $_POST['user'];
$sql = " selectユーザー名 = '".mysql_real_escape_string($username)."' およびパスワード ='".mysql_real_escape_string($pw)."' 制限 1";
$result = mysql_query($sql) ; のユーザーからの CTR として count(*) を実行します。
while ($data = mysql_fetch_object($result)){
if ($data->ctr == 1){
//アプリケーションに入っても大丈夫です
$okay = 1; if ($okay){
$_SESSION['loginokay'] = true;
header("index.php");
header("login.php")
?> () は、ユーザー入力への悪意のある SQL インジェクションを回避するために、ユーザー入力のラッパーとして機能します。ユーザーが SQL インジェクションを介して不正なパスワードを渡そうとすると、次のクエリがデータベースに渡されます:
select count(*) as ctr from users where username='foo' and password='' or '1'=' 1' 制限 1"
データベースにはそのようなパスワードに一致するものはありません。たった 1 つの簡単な手順を実行するだけで、Web アプリケーションの大きな穴がふさがれます。ここでの教訓は、SQL クエリに対するユーザー入力は常にエスケープする必要があるということです。
ただし、次の項目は、ユーザーによる GET 変数の操作を防止することです。前のセクションでは、ユーザーが不正なパスワードでログインできないようにするため、学んだテクニックを適用する必要があります。 SQL ステートメントに対するすべてのユーザー入力がエスケープされます
ただし、ユーザーが安全にログインしたからといって、ユーザーがルールに従って行動することを意味するわけではありません。 — たとえば、損害を引き起こす可能性は数多くあります。アプリケーションでは、ユーザーが特別なコンテンツを表示できるようにする場合があります。template.php?pid=321 の疑問符の後の場所を指すリンクは、クエリ文字列が URL 内に直接配置されるため、クエリ文字列とも呼ばれます。 GET クエリ文字列。PHP では、register_globals が無効になっている場合、$_GET['pid'] を使用して、template.php ページで、リスト 8 と同様の操作を実行できます。 php
$pid = $_GET['pid'];
//架空のクラス Page のオブジェクトを作成します
$obj = new Page;
$content = $obj->fetchPage($pid) );
//そして、ページを表示する PHP のバッチができました
ここで何か問題がありますか? まず、ほとんどのユーザーは、それを信頼しません。ただし、ブラウザの 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 変数全体が数値で構成されていることを確認するのがベスト プラクティスであることがわかっています。
リスト 10. 正規表現を使用して 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 = "pid='".mysql_real_escape_string($ pid) のページから pid,title,desc,kw,content,status を選択します。 "'";
}
}
?>
「PID が数値であることを確認したのに、なぜそれをエスケープする必要があるのか?」と疑問に思うかもしれません。 fetchPage() メソッドを使用します。このメソッドが呼び出されるすべての場所で保護を提供する必要があり、メソッド内でのエスケープは多層防御の意味を具体化します。
ユーザーが非常に長い値 (最大 1000 文字など) を入力してバッファ オーバーフロー攻撃を開始しようとした場合はどうなりますか?次のセクションでこれについて詳しく説明しますが、ここでは別のチェックを追加して、入力 PID が正しい長さであることを確認できます。データベースの pid フィールドの最大長は 5 桁であることがわかっているため、以下のチェックを追加できます。
リスト 12. 正規表現と長さチェックを使用して GET 変数を制限する
$pid = $_GET['pid'];
if (strlen($pid)){
if (!ereg("^ [ 0-9]+$ ", $ Pid) && Strlen ($ pid) & gt; 5) {
// ログアウトするかページに戻すなど、適切な処理を行います} {{/ /empty $pid,したがって、ホームページに送り返してください
}
//架空のクラス Page のオブジェクトを作成します。これは
//悪意のあるユーザー入力からさらに保護されます
$obj = new Page; ;fetchPage($pid);
//これで、ページを表示する PHP のバッチができました
?>
これで、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) ;
}
?>
なぜ 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 がありますが、長すぎる可能性があるので、
どうか確認しましょう(strlen($pid)>5){
$pid = substr($pid,0,5);
}
//架空のクラス Page のオブジェクトを作成します。これは
//さらに保護されます。邪悪なユーザー入力
$obj = new Page;
$content = $obj->fetchPage($pid);
// これで、ページを表示する 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;
}
?>
この一連の操作は少し厳密すぎると思われるかもしれません。結局のところ、16 進文字列には、外国語で文字を印刷するなど、正当な用途があります。 16 進正規表現をどのように展開するかはあなた次第です。より良い戦略は、1 行に 16 進文字列が多すぎる場合、または文字列が特定の文字数 (128 や 255 など) を超えた場合にのみ 16 進文字列を削除することです。
クロスサイト スクリプティング攻撃
クロスサイト スクリプティング (XSS) 攻撃では、多くの場合、悪意のあるユーザーが (または他のユーザー入力方法を介して) フォームに情報を入力し、悪意のあるクライアント側タグをプロセスまたはデータベースに挿入します。たとえば、サイトに訪問者が名前、電子メール アドレス、簡単なメッセージを残せる簡単なゲストブック プログラムがあるとします。悪意のあるユーザーは、この機会を利用して、他のユーザーにとって不適切な画像やユーザーを別のサイトにリダイレクトする JavaScript など、短いメッセージ以外のものを挿入したり、Cookie 情報を盗んだりする可能性があります。
幸いなことに、PHP には、HTML タグで囲まれたコンテンツを削除できるstrip_tags() 関数が用意されています。また、strip_tags() 関数を使用すると、 や など、許可されるタグのリストを提供できます。
リスト 16 は、前の例を基にした例を示しています。
リスト 16. ユーザー入力から HTML タグを消去する
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
}
?>
<入力タイプ="submit" name="submit" value="go"/>
セキュリティの観点から、公開ユーザー入力にはstrip_tags()を使用する必要があります。フォームが保護領域 (コンテンツ管理システムなど) にあり、ユーザーがタスク (Web サイトの HTML コンテンツの作成など) を正しく実行することを信頼している場合、strip_tags() の使用は不要であり、生産性に影響を与える可能性があります。
もう 1 つの質問: 投稿やゲスト登録項目へのコメントなどのユーザー入力を受け入れ、この入力を他のユーザーに表示する必要がある場合は、応答を PHP の htmlspecialchars() 関数に入れる必要があります。この関数は、アンパサンド、< > 記号を HTML エンティティに変換します。たとえば、アンパサンド (&) は & になります。この場合、悪意のあるコンテンツがフロントエンドのstrip_tags()処理を逃れたとしても、バックエンドのhtmlspecialchars()によって処理されます。
ブラウザ内データ操作
ユーザーがページ上のヘッダー要素やフォーム要素を改ざんできるようにするブラウザー プラグインの一種があります。 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
;label for="name">名前
hidden" name="ステータス" value="live"/>
< ;/ form>
隠し変数の 1 つがテーブル名である users を公開していることに注意してください。値 create を含むアクション フィールドも表示されます。基本的な SQL の経験がある人なら、これらのコマンドがおそらくミドルウェアの SQL エンジンを制御していることがわかります。大混乱を引き起こしたい人は、テーブル名を変更するか、削除などの別のオプションを提供するだけで済みます。
図 1 は、データ改ざんによってもたらされる損害の範囲を示しています。データ改ざんにより、ユーザーはフォーム データ要素だけでなく、HTTP ヘッダーや Cookie にもアクセスできることに注意してください。
図 1. Tamper Data ウィンドウ
このツールを防御する最も簡単な方法は、どのユーザーも Tamper Data (または同様のツール) を使用できる可能性があると想定することです。システムがフォームを処理し、フォームを専用のロジックに送信するために必要な最小限の情報のみを提供します。たとえば、登録フォームは登録ロジックにのみ送信する必要があります。
共通のフォーム処理関数を構築し、多くのページがこの共通ロジックを使用している場合はどうなるでしょうか?隠し変数を使用してフローを制御するとどうなるでしょうか?たとえば、非表示のフォーム変数で、書き込むデータベース テーブルや使用するファイル リポジトリを指定できます。オプションは 4 つあります:
何も変更せず、システムに悪意のあるユーザーがいないことを祈ります。
非表示のフォーム変数の使用を避けるために、より安全な専用フォーム処理関数を使用するように関数を書き換えます。
md5() またはその他の暗号化メカニズムを使用して、隠しフォーム変数内のテーブル名またはその他の機密情報を暗号化します。 PHP 側でそれらを復号化することを忘れないでください。
略語やニックネームを使用して値の意味を曖昧にし、PHP フォーム処理関数でこれらの値を変換します。たとえば、users テーブルを参照したい場合は、u または任意の文字列 (u8y90x0jkL など) を使用して参照できます。
後の 2 つのオプションは完璧ではありませんが、ユーザーがミドルウェア ロジックやデータ モデルを簡単に推測するよりははるかに優れています。
今残っている質問は何ですか?リモートフォーム送信。
リモートフォーム送信
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 = Strip_tags($ _POST ['name']); ,3})!", "",$input);
return $ clean;
}
?>
PHP ではセッション データをサーバー間で移行できないため、この手法は機能します。誰かがあなたの PHP ソース コードを取得し、それを自分のサーバーに移動し、あなたのサーバーに情報を送信したとしても、あなたのサーバーが受け取るのは、空の、または不正な形式のセッション トークンと、最初に提供されたフォーム トークンだけです。これらは一致しないため、リモート フォームの送信は失敗します。
結論
このチュートリアルでは多くの問題について説明しました:
SQL インジェクションの問題を防ぐために mysql_real_escape_string() を使用します。
正規表現と strlen() を使用して、GET データが改ざんされていないことを確認します。
正規表現と strlen() を使用して、ユーザーが送信したデータがメモリ バッファーをオーバーフローしないようにします。
strip_tags() と htmlspecialchars() を使用して、ユーザーが有害な可能性のある HTML タグを送信しないようにします。
データ改ざんなどのツールによるシステムの侵害を防ぎます。
一意のトークンを使用して、ユーザーがリモートからサーバーにフォームを送信できないようにします。
このチュートリアルでは、ファイル インジェクション、HTTP ヘッダー スプーフィング、その他の脆弱性などのより高度なトピックについては説明しません。ただし、ここで学んだことは、現在のプロジェクトをより安全にするために十分なセキュリティをすぐに追加するのに役立ちます。
参考資料
学習
Zend.com で役立つ PHP 101 チュートリアルを見つけてください。
Chris Shiflett の『Essential PHP Security』のコピーを入手してください。彼が行う紹介は、このチュートリアルよりもはるかに詳細です。
Simson Garfinkel の『Web セキュリティ、プライバシー、コマース』のコピーを入手してください。
PHP セキュリティ コンソーシアムの詳細をご覧ください。
「PHP セキュリティ上の間違いトップ 7」を読んでください。
developerWorks の「推奨 PHP 読書リスト」をチェックしてください。
developerWorks の記事「PHP の監査、パート 1: register_globals について」を読んでください。
PHP セキュリティ HOWTO ウェブキャストをチェックしてください。
PHP について詳しくは、IBM developerWorks の PHP プロジェクト リファレンスを参照してください。
developerWorks の技術イベントや Web キャストにご注目ください。
世界中の IBM オープンソース開発者向けの今後のカンファレンス、インサイダー プレビュー、Web キャスト、その他のイベントについて学びます。
developerWorks のオープンソース ゾーンにアクセスしてください。ここには、オープンソース テクノロジーを使用した開発や IBM 製品での使用に役立つ、豊富なハウツー情報、ツール、プロジェクトの更新情報が含まれています。
ソフトウェア開発者間の興味深いインタビューやディスカッションを聞きたい場合は、developerWorks ポッドキャストを必ずチェックしてください。
製品とテクノロジーを入手する
Windows ユーザーは WAMPServer をダウンロードできます。
PHP を使用して次の開発プロジェクトを構築します。
ダウンロードまたは DVD として入手できる IBM 試用版ソフトウェアを使用して、次のオープンソース開発プロジェクトを改善します。
ディスカッションする
developerWorks ブログに参加して、developerWorks コミュニティに参加してください。
著者について
Thomas Myer は、情報アーキテクチャ、Web アプリケーション開発、および XML コンサルティングを専門とする、テキサス州オースティンにある Web コンサルティング会社、Triple Dog Dare Media の創設者兼主要人物です。彼は、『No Nansense XML Web Development with PHP』 (SitePoint 発行) の著者です。
以上、起動できないアプリケーションと並列構成が間違っているアプリケーションについて紹介しました。PHP アプリケーションのセキュリティについて、起動できないアプリケーションや並列構成が間違っている場合も含め、違反してはいけない 4 つのセキュリティ ルールを紹介します。 PHP チュートリアルに興味がある場合は、このヘルプから学ぶことができます。