FuelPHP でのセキュリティ対策(1)

FuelPHP Advent Calendar 201120日目です。

昨日は、@regepan さんの「FuelPHPでメールフォーム雑感。」でした。今日は、FuelPHP でのセキュリティ対策について解説します。1回では終わりそうにないので、何回か続く予定です。


では、FuelPHP での主要なセキュリティ対策について解説していきます。この記事は FuelPHP 1.1 を前提としています。

FuelPHP では、デフォルトのセキュリティ機能として以下が用意されています。

  • デフォルトのセキュリティ機能
    • 出力フィルタ
    • URI フィルタ
    • SQL インジェクション対策

また、デフォルトではありませんが、以下のセキュリティ機能もあります。

  • セキュリティ機能
    • 入力フィルタリング
    • CSRF 保護
    • XSS フィルタリング
    • Validation クラス

Validation については、4日目の @fukata さんの「[FuelPHP]Validationの使い方」で解説されています。

それでは、具体的にそれぞれの機能を見ていきましょう。

出力フィルタ

出力フィルタは、ビューへ渡す変数を自動的に処理する機能です。デフォルトでこの機能はオンになっています。

設定ファイル config.php の security.output_filter で処理する関数やメソッドを指定します。デフォルトでは Security::htmlentities が設定されており、ビューへ渡される変数は、原則として、このメソッドで処理されます。

app/config/config.php

<?php
…略…
	/**
	 * Security settings
	 */
	'security' => array(
…略…
		/**
		 * This output filter can be any normal PHP function as well as 'xss_clean'
		 *
		 * WARNING: Using xss_clean will cause a performance hit.  How much is
		 * dependant on how much input data there is.
		 */
		'output_filter'  => array('Security::htmlentities'),
	),
…略…

具体的な実装は Security::htmlentities のコードを見るとわかりますが、最終的に、ビューに渡される文字列は htmlentities() でエスケープされることになります。

オブジェクトを渡した場合は、__toString() メソッドを持っていないとエラーになります。

例外として、config.php の whitelisted_classes でビューに渡せるクラスを設定できます。ここに設定したクラスのオブジェクトはそのままビューに渡され、自動での出力フィルタリングはされませんので注意してください。安易にここにクラスを追加しないようにしましょう。

app/config/config.php

<?php
…略…
	/**
	 * Security settings
	 */
	'security' => array(
…略…
		/**
		 * With output encoding switched on all objects passed will be converted to strings or
		 * throw exceptions unless they are instances of the classes in this array.
		 */
		'whitelisted_classes' => array(
			'Fuel\\Core\\Response',
			'Fuel\\Core\\View',
			'Fuel\\Core\\ViewModel',
			'Fuel\Core\Validation',
			'Closure',
		)
	),
…略…

【注意】なお、Security::htmlentities では、最終的な処理が htmlentities() 関数でなされますが、何故か第2引数は ENT_COMPAT になっていますので注意してください。

また、第4引数は false になっています(PHP のデフォルトは true)。第4引数は double_encode の指定であり、false はダブルエンコードしないという設定です。

$value = htmlentities($value, ENT_COMPAT, \Fuel::$encoding, false);

HTML をビューに渡したい場合

ビューに渡す変数は上記のように自動でエスケープされるわけですが、場合により HTML を渡したいこともあります。その場合には、View クラスの set_safe() メソッドを使うと、エスケープされずにビューに文字列を渡せます。

Controller を継承した場合は、

$view = View::forge('form/index');
$view->set_safe('error_html', $val->show_errors());
return $view;

Controller_Template を継承した場合は、

$this->template->content = View::forge('form/index');
$this->template->content->set_safe('error_html', $val->show_errors());

のようになります。ビューでは $error_html という変数にエスケープされない値が代入されています。

URI フィルタ

URI フィルタにより、入力された URI は自動的に処理されます。

デフォルトでは htmlentities が指定されていますが、これは、Security::htmlentities で処理されるという意味になります。

app/config/config.php

<?php
…略…
	/**
	 * Security settings
	 */
	'security' => array(
…略…
		'uri_filter'       => array('htmlentities'),
…略…

この処理により、コントローラに渡される時点では URI は、すでに HTML エスケープされています。

以下のようなテストコードで確認してみましょう。

app/classes/controller/uri.php

<?php

class Controller_Uri extends Controller
{
	public function action_index($a = '', $b = '')
	{
		$data['a'] = $a;
		$data['b'] = $b;
		
		Debug::dump($data);

		return Response::forge(View::forge('uri', $data));
	}
}

app/views/uri.php

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>FuelPHP Framework Test</title>
</head>
<body>
	<div id="header">
	</div>
	<div class="container">
	a: <?php echo $a; ?><br />
	b: <?php echo $b; ?><br />
	</div>
</body>
</html>

http://localhost/fuel/uri/index/a&b/b%3C%3E にアクセスしてみます。

コントローラの時点ですでにエスケープされていることがわかります。

また、エスケープされた値をビューに渡していますが、double_encode が false で処理されるため、ビューでも二重にはエスケープされません。

FuelPHP でのセキュリティ対策(2) へ続きます。


Advent Calendar の明日は、@madmamor さんの「FuelPHPのTasks」の予定です。お楽しみに!