次に、sample1の「確認画面(confirm.php)」でポイントとなる個所を解説します。
1 <?php |
1〜3行目
ここでも、1行スクリプトを用いてPHPコードを埋め込んでいます。1〜3行目は注文番号が適正にセットされているか「isset($変数)」で確認し(2行目)、不正操作を判断しています。
例えば、メニュー画面(menu.php)からの呼び出しではなく、確認画面(confirm.php)から直接呼び出された場合には、$order_idは何もセットされておらず初期化されていません。isset()は、変数が初期化されていなければFALSEを返し、初期化されていればTRUEを返します。戻り値がFALSEであれば、die("メッセージ")で処理を中断します。
ちなみに、isset()は変数が初期化されていれば空であってもTRUEを返すため、単純な判定に限定されます。本格的な用途では、注文番号のけた数やチェックデジットを用いるなどします。
(省略) |
49、59、67、75、83行目
メニュー画面(menu.php)で入力された値を、完了画面(purchase.php)に引き渡すため、<input type="hidden"...>タグを利用し再びフォームに埋め込みます。
(省略) |
62行目
文字列の連結には「.」を用います。
(省略) |
63、93〜95、98〜100行目
各アイテムの小計や合計金額などの四則演算には、「+」「-」「*」「/」を用います。PHPは格納される値によって型を自動に判断するため、$_POST[""]に整数値が格納されていれば、「+」「-」「*」「/」を用いることができます。またprint文では、整数型でも明示的に型変換することなく文字列として出力されます。
続いて、sample1の「完了画面(purchase.php)」でポイントとなる個所を解説します。ちなみに、1〜41行目、56、63、66行目でPHPコードを使用しています。56、63、66行目は単に値を表示しているだけなので1〜41行目に絞って解説します。
1 <?php |
3行目
注文番号の確認は、確認画面(confirm.php)と同様に「isset()」で行います(3行目)。注文番号に問題がなければ、受注内容をファイルに書き出します。sample1では受注データを「/tmp/order注文番号.txt(注文番号が1000000001なら/tmp/order1000000001.txt)」に書き出します。すでに同名ファイルが存在している場合は、注文済みと判断します。
「purchase.php」を再読み込みさせるか、前画面に戻って再度「購入」ボタンをクリックすると、二重発注と見なされ、「すでに注文済みです」と表示されます。
5、13〜20行目
選択された各アイテムの個数を$_POST["apache"]、$_POST["qmail"]、$_POST["mysql"]、$_POST["bind"]で取得し、氏名と住所を$_POST["name"]と$_POST["address"]で取得します。
取得された値はそのままファイルに書き出しますが、念のため「escapeshellcmd("文字列")」用いてLinuxシェルに特別な意味を持つ文字をエスケープします(注)。
注:例えば、確認画面(confirm.php)の名前や住所欄に「rm -rf *;」などと入力する、escapeshellcmd()により「rm -rf \*\;」と置き換えられます。
また、シェルに影響する文字と同様に<HTML>タグとして意味を持つ文字列もエスケープする必要があります。<HTML>タグのエスケープには「htmlspecialchars()」を用います。
「<form>」という記述であれば、「\<form\>」のような内容に書き換えられます。例えば確認画面(confirm.php)の住所欄で「<form action="悪意のあるスクリプト">〜<input type="submit"></form>」などと入力された場合などに確認画面でフォームがそのまま表示されるような事態を防ぎます。
12行目
受注データには注文された日付や時刻を記録する必要があるため、現在日時を「time()」で取得して「date()」で「2007/02/10 22:15:30」のようにフォーマットをそろえます。
22〜32行目
値の精査が完了したところで、ファイルに書き出す文面としての文字列を用意します。今回、文字列の生成にヒアドキュメントを使用しています。ヒアドキュメントは、比較的大量の文字列を扱う場合に用いられます。「<<<識別子」〜「識別子」までの文字を文字列に格納します。
34〜39行目
ファイルへの書き出しは、メニュー画面(menu.php)のカウントデータの書き出しと同様にfopen()、set_file_buffer()、flock()/fputs()、flock()/fclose()の順で行います。
実際にsample1を動作させて、「/tmp」に受注データが書き出されているかどうかを確認します。日本語が表示されない場合は、使用しているターミナルのキャラクターセットを「UTF-8」に変更します。
ご注文時刻:2007/02/11 00:18:31 |
sample1では、<input type="hidden"...>タグでフォームに値を埋め込み、前々の画面の入力値などを最後の完了画面(purchase.php)に引き渡すようにしました。
このようなケースでは、クライアント側でデータが改変される可能性を考慮する必要があります。ここで紹介したサンプルは、同一サーバ上ですべてのPHPスクリプトが実行されることを想定していますが、何も対策していない場合には次のような処理が可能となります。
http://他者のサイト/confirm.phpを模したサンプル |
http://オンラインストア/purchase.php |
sample1では、単に$order_idが埋め込まれているかを確認しているのみです。変数名はフォーム中<input>タグのname属性から簡単に類推できます。そのため、環境変数からRefererを拾い(コラム1参照)、参照元が自サイトかどうかを確認するなどの煩わしい処理が本来は必要です。
また、ブラウザで再読み込みをされた場合や[戻る]ボタンで前画面に戻られた場合には、表示されるキャッシュデータも考慮する必要があります。こうしたクライアント側でデータを保持する方法には、悪意あるデータ改ざんの危険性があります。
Refererを調べることで、どこからページにアクセスしたのかを調査できます。PHPではスーパーグローバル変数「$_SERVER」から取り出すことができます。
<?php |
また、PHPを使わずにApacheの「.htaccess」を利用して、参照元を制限できます。Apacheの設定や.htaccessの記述方法については、「実用 Apache 2.0運用・管理術 最終回 『接続数/帯域制限で無法なダウンローダを撃退』」を参照してください。
Copyright © ITmedia, Inc. All Rights Reserved.