掲示板投稿内容をチェックする:入力値検証
ここまでで一通りの機能を備えた掲示板らしくなりましたが、あらゆる入力を許している状態です。しかしセキュリティのためにも、入力された値の検証は欠かせません。symfonyでは入力値検証のための設定もYAMLファイルで行うようになっています。
YAMLファイルによる入力値条件の設定:「validate/~.yml」
特定のアクションにおける入力値検証設定は、モジュールフォルダ内の「validate/<アクション名>.yml」ファイルを作成することによって行われます。今回は入力された値が実際にデータベースに書き込まれるupdateアクションにおいて入力値検証を行うことになります。
update.ymlの作成
validateフォルダ内に以下の内容の「update.yml」ファイルを作成します。下記のように、まず検証を行う入力方法(post、get、またはその両方)を指定し、次に各フィールドについて検証条件を設定します。
# post, get双方の入力に対応する methods: [post, get] # 各フィールドの入力値条件を以下に記述する fields: # フィールド名 title: # 必須項目であることを示す required: # 入力されていない場合表示するメッセージ msg: タイトルを入力してください。 # 使用するバリデーター。sfStringValidatorは文字列用 sfStringValidator: # 許容最小文字数。指定しなければ1文字 min: # 最小文字数より少ない場合表示するメッセージ min_error: # 許容最大文字数。指定しなければバリデーター上無制限 max: 50 # 最大文字数を超えた場合表示するメッセージ max_error: タイトル文字数が多すぎます(50文字以内)。 author: required: msg: お名前を入力してください。 sfStringValidator: min: min_error: max: 30 max_error: お名前の文字数が多すぎます(30文字以内)。 mail: # emailアドレス用バリデーター sfEmailValidator: # 適正なemailアドレスが表示されない場合表示するメッセージ email_error: "適切なメールアドレスを入力してください (例: name@domain.com)。" url: # 正規表現用バリデーター sfRegexValidator: # パターンマッチした場合には入力値は適正であるという指定 match: Yes # 適性でない場合表示するメッセージ match_error: "適切なホームページアドレスを入力してください (例: http://example.com)。" #マッチさせる正規表現 pattern: /^https?:\/\/\w+/i body: required: msg: 本文を入力してください。 passwd: required: msg: 削除用パスワードを入力してください。 sfStringValidator: min: 5 min_error: 削除用パスワードの文字数が少なすぎます。5文字~15 文字で入力してください。 max: 15 max_error: 削除用パスワードの文字数が多すぎます。5文字~15文 字で入力してください。
各フィールド内に記述されているバリデーターは例えば以下のものが利用できますが、カスタマイズする(カスタムバリデーター)ことも可能です。カスタムバリデーターについては、次回以降で扱う予定です。
バリデーター | 内容 | パラメータ | パラメータの意味 |
sfStringValidator | 文字列を検証する | min: | 最小文字数 |
min_error: | 最小文字数より少ない時のエラーメッセージ | ||
max: | 最大文字数 | ||
max_error: | 最大文字数より少ない時のエラーメッセージ | ||
sfEmailValidator | Emailアドレスの妥当性を検証する | email_error: | Emailアドレスが適切でないときのエラーメッセージ |
sfRegexValidator | 正規表現を指定し検証を行う | match: | マッチした場合に適正と判断するならYes、不適正と判断するならNo |
match_error: | 不適正な場合のエラーメッセージ | ||
pattern: | マッチさせる正規表現 | ||
sfCompareValidator | 2つの入力が同一かを検証する(パスワードを2カ所入力させ双方が一致するかなど) | check:, | 比較対照となるフィールド |
compare_error: | 同一でない場合のエラーメッセージ |
handleErrorUpdate関数作成(actions.class.php内追記)
もしここに記された入力条件に合わない入力があった場合、アクションクラス内のhandleError<アクション名>
関数が呼び出されます。
public function handleErrorUpdate() { $this->forward('bbsdata', 'create'); }
この場合、再度入力してもらうべく、createアクションへフォワードしています。
テンプレートの修正
入力値が適正でない場合は上記handleError
関数でcreateアクションへ差し戻されますが、デフォルトではエラーとなった場合にそれがどのようなエラーなのか表示されず、またそれまで入力したフォームの内容は白紙に戻されてしまいます。symfonyには「/validate/~.yml」で設定したエラー内容を表示する関数が用意されているので、入力画面上にそれを表示するようにします。また、それまで入力された内容も保持されるよう、テンプレート「editSuccess.php」を書き換えます。
<?php // auto-generated by sfPropelCrud // date: 2006/12/07 00:14:58 ?> <?php use_helper('Object') ?> // 入力値検証の結果が適性だったかどうか <?php if ($sf_request->hasErrors()): ?> <p>投稿されたいずれかの項目が適切ではありません。 下記のエラー内容を訂正し再投稿してください。 </p> <ul> //生じたエラーを表示 <?php foreach($sf_request->getErrors() as $error): ?> <li><?php echo $error ?></li> <?php endforeach ?> </ul> <br /> <br /> <?php endif ?> <?php echo form_tag('bbsdata/update') ?> <?php echo object_input_hidden_tag($bbsdata, 'getId') ?> <table> <tbody> <tr> <th>タイトル:</th> <td> <?php if ($sf_params->get('oya_title')): ?> <?php echo input_tag('title', 'Re: '.$sf_params->get('oya_title'), array ('size' => 30,)) ?> //$sf_paramsでそれまで入力されていた内容を表示 <?php else: ?> <?php echo input_tag('title', $sf_params->get('title'), array ('size' => 30,)) ?> <?php endif; ?> </td> </tr> : : <tr> <th>削除用パスワード:</th> <td> <?php echo input_tag('passwd', $sf_params->get('passwd'), array ('size' => 20,)) ?> </td> </tr> </tbody> </table> : :
$sf_request
ショートカットで前回送られたリクエストについてのエラー情報を取得し、入力されていた内容の表示には$sf_params
ショートカットを用いています。今回用いた$sf_request
/$sf_params
ショートカットの関数は次のとおりです。
関数 | 内容 |
$sf_request->hasErrors() | エラーが生じれば真 |
$sf_request->getErrors() | 生じたすべてのエラー内容を配列で返す |
$sf_params->get('<name>') | 'name'で送られてきたデータを返す |
実際には、次のようなエラー表示になります。
symfonyのセキュリティについて
ここまでで、入力値検証を行うようになりました。しかしまだ、HTMLタグなどの入力に対しては無防備です。ですが、これに対し入力値を一つ一つ、htmlspecialchars
などでエスケープする必要はありません(初回はビュー出力ではないため行いましたが)。「view.yml」内に以下を追記する事で、エスケープ処理は自動的に行われます。
escaping: strategy: both method: ESC_ENTITIES
strategy
の部分はエスケープ処理の対象を指定します。「both
($sf_data
コンテナとローカルスコープ変数に適用)」「bc
($sf_data
コンテナのみに適用)」「on
(変数は$sf_data
コンテナからしか得られなくなる)」「off
(エスケープ処理を行わない)」から選びます。
method
の部分はエスケープ処理の方法を指定します。「ESC_RAW
(エスケープ処理を行わない)」「ESC_ENTITIES
(phpのhtmlentities
関数を用いる)」「ESC_JS
(HTMLとして使用されるであろうJavaScript文字列に埋め込まれる文字列をエスケープする)」「ESC_JS_NO_ENTITIES
(JavaScript文字列に埋め込まれる文字列をエスケープするが、エンティティーは付加しない)」が選べます。
$sf_data
コンテナとは、テンプレート内で変数を表示するためのコンテナです。例えば「$x」を表示する際に、echo $sf_data['x'] echo $sf_data->get['x']
echo $sf_data->getRaw['x']
<?php echo $sf_data->getRaw('sf_content') ?>
不要な要素の削除
ここまで、入力値検証を含め基本機能を一通り実装してきましたが、今まで残しておいたshowアクション関連や書き込み以外の編集機能、レコードを直接削除するdeleteアクションは不要なので、ここで削除します。
ID表示部分のリンクをはずす
scaffolding機能で自動生成したlistビューではID表示部分にshowアクションへのリンクが張られていましたが、これをはずします。
<td><?php echo $bbsdata->getId() ?></td> : : <td><?php echo $reply->getId() ?></td>
editSuccess.php不要部分消去
新規投稿や返信機能に使用する「editSuccess.php」ですが、既存レコードの編集には使用しない(つまり、Objectヘルパーも使用しない)ので、関連項目を削除します。
<?php use_helper('Object') ?> <?php echo object_input_hidden_tag($bbsdata, 'getId') ?> : : <?php if ($bbsdata->getId()): ?> <?php echo link_to('delete', 'bbsdata/delete?id='.$bbsdata->getId(), 'post=true&confirm=Are you sure?') ?> <?php echo link_to('cancel', 'bbsdata/show?id='.$bbsdata->getId()) ?> <?php else: ?> //この行のみ残す <?php echo link_to('cancel', 'bbsdata/list') ?> <?php endif; ?>
投稿後リストへ直接接続(executeUpdate)
scaffolding機能でのUpdateアクションはshowアクションにリダイレクトされますが、これをlistアクションへのリダイレクトに書き換えます。
return $this->redirect('bbsdata/show?id='.$bbsdata->getId());
return $this->redirect('bbsdata/list');
他、不要なアクション/ビューの削除
仕上げに、showアクション/ビューとdeleteアクションを削除します。
- 「executeShow()」削除
- 「showSuccess.php」削除
- 「executeDelete()」削除