FuelPHPのtips
FuelPHP SimpleAuthをいじっていたらブラウザが310リダイレクトループエラーに
0何かの拍子に、SimpleAuthがリダイレクトエラーでログインできなくなってしまった。
閲覧履歴を削除してもダメ。
一旦データベースのusersテーブルとmigrationsテーブルを削除して$ oil r migrate
したら直った。
詳しく調べていないので原因は判りません。
FuelPHP tips Fuelのセキュリティ機能2
0・出力フィルタ
・URIフィルタ
・SQL文の自動エスケープ
・入力フィルタ
・XSSフィルタ
・バリデーション(検証)
・CSRF保護
はい、Fuelのセキュリティ機能1で説明できなかった残り4つ行きます。
微妙に順番が変わってるのは気にしない。
1. 入力フィルタ
入力フィルタとは、スーパーグローバル変数の内
$_POST
$_GET
$_COOKIE
この3つを処理する機能です。
フォームからの入力やクッキーの値などに独自のフィルタをかける事が可能です。
デフォルトではなにも設定されておらず全く機能しません。
1 2 3 4 5 6 |
/** * Security settings */ 'security' => array( 'input_filter' => array(), ) |
ほら、coreの方のconfig見ても何もないでしょ?array()の中が空でしょ?
この中には自分で作った関数を入れる事ができます。(使う時はcoreじゃなくappの方を編集しましょう。)
例えば、出力フィルタやURIフィルタで使用されている「htmlentities」を指定できたりします。(公式ドキュメント引用)
このブログの住人が参考にしている書籍では文字エンコーディングのフィルタを用意していましたね。
指定の仕方は例えば次のようになります。
1 2 3 4 5 6 |
/** * Security settings */ 'security' => array( 'input_filter' => array('Ore::rule'), ) |
Oreクラスのruleメソッドを指定してます。
でもって次がそのクラスです。配置は app/class/ore.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php class Ore { public static function rule($value) { /*ここでフィルタのルールを書いてやるといいと思うんだ*/ /*例えば、$valueの文字エンコードを調べたり*/ /*改行やタブはいいとしても、Delete等の普通は入力できない制御文字のチェックをするといいです*/ $foo = rand(); if( $foo % 100 != 0) return $value; else die('素晴らしく運がないなぁ君はぁ'); } } |
ランダムで100分の1の確率で処理を受け付ける的な感じになってます。
サンプルです。あくまでサンプルです。
普通だったら文字エンコーディングのチェックとかすると思います。
なぜこの入力フィルタがデフォルトで有効になってないかというと、公式ドキュメントが
「そんなことより出力フィルタが先だ」って言ってた気がする。
最終的に出力フィルタで全部エスケープするよ。って事だろうか。
2. XSSフィルタ
さぁ、なんて読むだろうか。
クロスサイトスクリプティングと読むらしい。
ちなみに、Fuelのセキュリティ機能1の出力フィルタでやった危ないサンプルがあったけど、あれがXSSです。
出力フィルタで使用しているhtmlentitiesだけでは対処が難しい場合、
Securityクラスが用意している「xss_clean()」っていう関数を使えばいいらしいです。
htmLawedというライブラリを使用してるらしいです。
なんでもかんでもタグ文字とかをエスケープするのではなく、危険なコードを安全なものに直したりするらしいんですがショウジキヨクワカラナイシシュツリョクフィルタニタヨリッキリナノデパスデオネガイシマス。
一応サンプル(Viewは割愛)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php class Controller_Test extends \Controller { public function action_index() { //以下サンプル3つ。 $data['a'] ='<SCRIPT>alert("(・ω・)")</SCRIPT>'; $data['a'] = Security::xss_clean($data['a']); $data['b'] ='<div>これは大丈夫</div>'; $data['b'] = Security::xss_clean($data['b']); $data['c'] ='<A HREF="javascript:document.location=\'http://www.google.com/\'">XSS</A>'; $data['c'] = Security::xss_clean($data['c']); //出力フィルタをfalseにしておこう return View::forge('sec_test_view.smarty',$data,false); } } |
各3つのサンプルの2行目、xss_clean()の行をコメントアウトしたりして動作を確認できます。
1つ目はスクリプト実行によりアラートが出てきます。
フィルタに通すと「<SCRIPT></SCRIPT>」を取り除いてくれます。
2つ目はそのまま表示されます。全てのタグを取り除くわけではないようです。
3つ目はフィルタを通すと「javascript」の前に「deny:」を付けて無効化(?)してくれたりします。
「<A HREF=・・・」は「<a href=・・・」とか小文字に直してくれて残してくれます。
FuelPHPの公式ドキュメントには、「パフォーマンスの理由」により個別の変数に対して使用することを推奨しているようです。
それよりもなによりも出力フィルタを優先すべきだと言ってるので、出力フィルタをオフにしないように心がけたい。
3. バリデーション (検証)
バリデーションは、あらかじめ作って置いたルールに基づき変数を検証することです。
「ユーザー名は○文字以下まで」
「パスワードは○文字以上○文字以下」
などです。
使い方はこちらを参照してください。
→チュートリアル フォームの作成とDBの使い方
4.CSRF保護
このCSRF、クロスサイトリクエストフォージェリって言います。
XSSと似てますが違います。
CSRFの恐怖はこちらを参照→Wikipedia:クロスサイトリクエストフォージェリ
このCSRFの対策とし
1.ページ作成時に次の2のためのトークンを生成
2.form送信の時にCSRF対策用のトークン(hiddenでおk)を一緒に送信
3.送られてきたトークンをチェックし有効ならそのまま、無効ならアウトォォォ!
では、1のコードから2の送信
1 2 3 4 5 |
<form 〜〜割愛〜〜> <?php echo Form::hidden(Config::get('security.csrf_token_key'), Security::fetch_token()); ?> <input 割愛〜〜> </form> |
Viewで作ってます。面倒なんで。
Config::get(〜〜〜)で「security」の「csrf_token_key」の値を取得します。
デフォルトでは「fuel_csrf_token」という文字列になってます。
Security::fetch_token()でトークンを生成しています。
そして、各フォームの値と一緒に送信します。
そして、次の3のコードでチェック
1 2 3 |
if( Security::check_token() == false ) echo "トークンが一致しません。"; else "トークンが一致。有効なリクエストだぁ"; |
メッセージが長くてうざいけど、チェックする処理自体は非常に短いです。
このCSRF保護機能ですが、中身の実装がやや怪しいらしいです(暗号論的疑似乱数生成とか)
バージョンアップで強化されていくことを願いましょう。
これで一応終了。
もうセキュリティって言葉ってなんだ?
っていうゲシュタルト崩壊が起きてきそうです。
FuelPHP tips Fuelのセキュリティ機能1
0そろそろFuelPHPにも慣れてきたんじゃないっすかぁ?
じゃぁちょっとセキュリティとかっていう面倒くさいものをやりましょうか。
大丈夫大丈夫!FuelPHP優秀だから大丈夫だって!諦めんなよ!あっつくなれよ!
FuelPHPにあるセキュリティ:Security
FuelPHPには最初から以下のセキュリティ機能が入ってたりします。
・出力フィルタ
・URIフィルタ
・SQL文の自動エスケープ
・入力フィルタ
・CSRF保護
・XSSフィルタ
・バリデーション(検証)
上3つの太字で表したものはデフォルトで有効になっています。
多分意識してないと思いますが、コントローラーからビューに変数を渡すときに出力フィルタが働いています。
1つ1つ見ていきましょうか。
1. 出力フィルタ
Securityクラスにhtmlentities()って関数があって、出力フィルタとURIフィルタで使ったりしてます。
この関数は文字列をエスケープして返します。
なんのこっちゃって言う人のために、以下のサンプルをどうぞ。(Viewは適当に作ってね★)
1 2 3 4 5 6 7 8 9 10 |
/*action_index以外省略*/ public function action_index() { //例えばユーザが入力した値を表示するものとする //↓Inputクラスで取得したりしたものとする $data = "<script>alert('(・ω・)')</script>"; //↓第3引数でfalseを指定すると出力フィルタがオフになる return View::forge('test_view',array('data'=>$data) ,false); } |
このサンプルのページにいくと、javascriptのalertが出てきて「(・ω・)」とかふざけた顔文字が出てくる。
顔文字だったらカワイイだけで済むが(かわいいよねこれ(・ω・))
これをユーザーが自由に入力できるとしたら攻撃されまくりである。
例えばユーザー名に
「<script>有害コード</script>」
って入力されたりするかもね。
なんかヤバい感じを味わったら、次は
1 2 3 |
//↓第3引数でfalseを指定すると出力フィルタがオフになる //return View::forge('test_view',array('data'=>$data) ,false); return View::forge('test_view',array('data'=>$data) , true); |
さっき作ったサンプルの上の部分を変更してみよう。
第3引数をfalseからtrueにするのだ。
すると、今度はサンプルで書いたScriptのコードがそのまま出てきたと思う。
ブラウザの機能の「ソースコードを表示」で見てみると、不思議な文字の羅列があったりする。
これは出力フィルタがhtmlタグをそのまんまじゃなく、それを示す違う記号にエスケープしたのです。
ちなみに、第3引数を省略すると、デフォルトでtrue設定になるので安心だ(・ω・)b
便利ですね。
2. URIフィルタ
URIフィルタとは。
まず以下のサンプルをどうぞ
1 2 3 4 5 6 7 8 9 10 11 12 |
class Controller_Test extends Controller { public function action_index($a='') { $data = "<>以外が入力された?"; if($a === Security::htmlentities("<>")) $data = "URIフィルタOn"; else if($a === "<>") $data = "URIフィルタおっふ"; return View::forge('test_view',array('data'=>$data)); } } |
これで 「http://~~~~~/test/index/<>」というURLにアクセスしましょう。(~~~の部分は当然各環境に合わせてください)
action_indexは本来指定しなくてもアクセス出来ますが、パラメーターを$aとして受けとるために記述が必要になってきます。
そしてパラメーターとして「<>」を渡しています。(あくまで例です。他にも試してみてください。)
サンプルでは、この「<>」をSecurityクラスのhtmlentitiesを使用して、URIフィルタが機能しているかどうかを判定しています。
まぁぶっちゃけ”URIフィルタおっふ”は表示される事はないだろう。。。or
オンとかオフとか書きましたが、このURIフィルタをオフにする方法がちょっと見つからなかったので設定はデフォルトのままで放置します。
3. SQL文の自動エスケープ
FuelPHPではSQLインジェクションに対する機能があります。
データベースを扱う部分で説明すると思いますが、クエリビルダーがその役目を負っています。
例えば「DB::select()->・・・」で始まる奴とかですね。
このクエリビルダー機能で発行されたクエリは全てエスケープ済みです。
DBクラスのクエリビルダーってすごく簡単でしかもセキュリティ対策もしてくれてるのです!
いやぁSQL文使わなくても(使えなくても)いいんですよー。
あとの4つはFuelのセキュリティ機能2で説明します。
FuelPHP tips Numクラス
0Uploadクラスとかで、なんかファイルサイズを指定したりする時どんな風に書いてますか?
$size = 1024*1024*8;
とか書いてませんか?
けどこの書き方、なんか数字の羅列で見づらいですね。
慣れてる人は8MBだ、ってすぐ出てくるかもしれませんね。
今回ご紹介する商品はこちら!
FuelPHPに最初から入っている!
Numクラス!
まだ、ちょっとバイトとかビットの数字に慣れてないです・・・
っていうそこのあなた!
これさえあればもう面倒くさい計算なんてしなくていい!
先ほどの例を上げると
$size = Num::bytes(‘8M’);
ほらスッキリ!
文字列を数値に自動で!変換してくれちゃったりするんですよ〜。
みなさん。驚くのはまだ早いですよ?
今回はこれにプラスして!逆変換機能もつけちゃいます!
echo Num::format_bytes(‘1024’); → 1kB
数値を入れればそれを文字列に直してくれます!
さらに!
echo Num::format_bytes(‘1124’,1); → 1.1kB
このように第2引数で小数点以下表示も可能!
けどやっぱり、みなさんが気になるのはお値段の方ですよね〜?
大丈夫です。このNumクラス、
数値→文字列機能
文字列→数値機能
2つセットで!15回払い対応の!0円!
はい、通販番組風の前置きが長くなりましたがNumクラスについて書きます。
Numクラス
数値に関する、型変換や出力を支援してくれます。
::bytes($size=0)
引数で指定した文字列を数値(float)で返します。実はintやfloatも引数として扱えます。
「K,M,G」や「MiB」なんていう特殊なものもあります。
::format_bytes($bytes=0 , [$decimals=0])
第1引数で指定した数値を文字列で返します。その際適切な接頭辞が付けられます。
第2引数は省略可。数値を指定すると小数点以下が表記されます。
例)
1 |
echo Num::format_bytes(1124,1); //→1.1kB |
::quantity($num, [$decimals=0])
第1引数で指定した数値を適切な接頭辞が付けた上で四捨五入します。(多分)
第2引数で数値を指定すると、どこで四捨五入するか小数点以下の場所を指定できます。
例)
1 |
echo Num::quantity('1624006',5); //→1.62401M |
::format($string = “” , $fomart = “” )
見づらいんで↑シングルクォーテーションをダブルクォーテーションにしました。
まずは例)
1 2 3 |
echo Num::format("1234567890" , " (000) - 000 - 0000"); //→ (123) - 456 - 7890 echo Num::format("1234567890" , "0!0#0$0%0&0'0(0)0~,.<>-^;:@[]日本語 0"); //→1!2#3$4%5&6'7(8)9~,.<>-^;:@[]日本語 0 |
なお第2引数の0の羅列は英数字ならなんでもいいらしい。逆を言うと英数字で分けたりとかできない。
日本語で分けれるのが笑える。
(“20120128″,”0000年00月00日”)で2012年01月28日とか出ちゃう。
追記:mask_stringで実験してからこっちも試したらなんかこうなった
1 |
echo Num::format("abcdefghij", "aaa-aaa-aaa-a"); //→abc-def-ghi-j |
::mask_string($string=”” , $format=”” , ignore=””)
例)
1 2 3 |
echo Num::mask_string("1111222233334444" , "**** **** **** 0000"); //→ **** **** **** 4444 echo Num::mask_string("111122223333" , "****-0000-****" , "-"); //→ ****-2222-**** echo Num::mask_string("aaabbbccceee" ,"*** aaa *** 000"); //→ *** bbb *** eee |
数値でも文字列でも可能のようです。
数値は「0」、文字列は「a」とかそういう指定は無いみたいです。
入力された英数字は、formatの英数字部分で表示され、それ以外ではその記号が出力されるみたいです。
ignoreで指定した記号はその際文字数としてカウントしないで無視されるようです。
なのでこんな例も可能
::mask_string(“abcdefghij”, “@@@-@@@-00@” ,”-“); → @@@-@@@-gh@@
お、クレジットカードとかに使えそうじゃん?とか思ったあなた。次の関数を見てくれ。
::mask_credit_card($string , $format=null)
クレジットカード用のmask_string関数が用意されてましたとさ。
例)
1 |
echo Num::mask_credit_card("1111222233334444"); //→ **** **** **** 4444 |
用意周到ですな・・・
基本「format()」を使えばなんとなる。
他にもちょくちょくありましたが、クレジット用みたいにformat()を使いやすくしたっていうものなんで省略します。
番外編:接頭辞
途中で1MiBとかいう特殊な表記が出てきましたが、これには深いわけが。
この接頭辞(k,M,G,Tとかね)って実は2を冪乗とする数値には使っちゃいけないって決まりがあったりする。
なのでコンピュータの世界では1MBって表記するのはダメだったりする。
さり気にキロだけ小文字なのはこれも決まりなのである。「大文字を使ってはいけない」らしい。
大文字にするとケルビンの方になるらしい。
コンピュータ専用の接頭辞があってそれを2進接頭辞と呼びます。
1kB→1KiB
1MB →1MiB・・・
という感じに「i」が付いちゃう。読み方は1KiB「1キビバイト」 1MiB「1メビバイト」という感じになります。
実際は、全然普及してないっぽいよね。Linuxだとけっこうお目にかかりますが。
エクサなんてエクスビってなるんだよ。(読みづらいのがさらに読みづらくなった)
ちなみに接頭辞には1da(デカ 10の1乗)とか不思議なものがある。
FuelPHP tips Uploadクラス
0FuelPHPでのアップロードについて書きます。
FuelPHPにはUploadクラスというものがあり、簡単にアップロードについて処理を行う事が出来ます。
サンプルでも書いてみます。
まずViewから。up_viewという名前で。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" > <title>sample_upload</title> </head> <body> <form action="upload" method="post" enctype="multipart/form-data"> ファイル:<input type="file" name="upfile" id="upfile"/> <input type="submit" value="アップロード" /> <?php echo $file_size; ?> </form> </body> </html> |
よし。
次にコントローラー。uploadtestというクラスで。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?php class Controller_Uploadtest extends \Controller { public function action_index() { //省略 /*ここでアップロードページを表示してあげるといいかも*/ return View::forge('up_view',array('file_size'=>0)); } public function action_upload() { //Uploadクラスの設定 $config=array( 'path'=>DOCROOT.'/file', 'auto_rename'=>true, 'ext_whitelist'=>array('jpg','jpeg','gif','png'), 'max_size'=>1024*1024*2, ); //上記の設定で$_FILEの情報を取得 Upload::process($config); //検証がOKなら処理に入る if(Upload::is_valid()){ //上記の設定でアップロードされたファイルを「path」で指定した場所に保存します。 Upload::save(); $data = 0;//例のための変数 //アップロードされたファイルの各種情報を取得します。 foreach (Upload::get_files() as $file){ /*ここにいろいろ処理を書く*/ //例:アップロードされたファイルの合計ファイルサイズを表示する。 $data = $data + $file['size']; } //例の続き return View::forge('up_view',array('file_size'=>$data)); } } } |
「例」って書いてるところは今回のサンプルのための変数とかです。ファイルサイズ表示します。
見ての通り、普通のコントローラーの中でUploadクラスを呼び出してるだけです。
ファイルサイズや拡張子を指定して、ファイル保存して、ファイルがあるかどうかチェックして、ファイルサイズを取得する。っていうのをちょっと生のPHPで検索してみるといいですね。
簡単ですね。
大体はコメントアウトの説明で分かるかと思います。
configで設定しているところ(15行目)
・path:ファイルの保存場所。save()関数で保存します。
・auto_rename:名前が同じファイルがアップロードされた時、末尾に「_1」「_2」を追加するかの設定。
・ext_whitelist:許可する拡張子。
・max_size:最大ファイルサイズ(※最後の方にちょっと注意書きます)
そして、Upload::process()に引数として渡してあげると、デフォルトの設定を上書きして処理してくれます。
このconfigを毎回設定するのは面倒ですね。
なので、core/config/にupload.phpがあるので、それをapp/config/にコピーして、いろいろ中身を書き換えればそれがデフォルト設定となります。
Upload::is_valid() (26行目)
バリデーションなのかバリッドって読むのかスマイルスマイルヴェリィドゥって読むのか分かりませんが。
つまりはconfigで設定した条件に合うかどうかのチェックです。
trueかfalseが返ってきます。
例の条件で言うと、「virus.exe」とかだと拡張子が条件に合わないのでfalseが返ってきます。
(ちなみに、拡張子なし「sample」ってファイルとかでもfalseが返ってきましたね。)
あとは、1MBを越えるファイルでもfalseが返ってきます。
Upload::get_files() の部分(33行目)
このget_files()は、ファイルの情報を取得します。
foreachを使ってるいるのは、引数なしでget_files()を使うと、アップロードされた全てのファイルを配列にして返してくるからです。
(確実に1つ以上アップロードされている場合get_files(0)とか出来ちゃう。)
33行目では、関数から返ってくる値を$fileとして受け取っています。
$fileはアップロードされたファイルの各種情報を収めた連想配列です。
1 2 3 4 5 6 7 8 9 10 11 12 |
name {string} ファイル名(拡張子あり) type {string} FILE_TYPE error {boolean} エラーの有無 size {int} ファイルサイズ field {string} アップロードファイルのフィールド名 file {string} 保存されているパス errors {array} エラーの配列 extension {string} 拡張子 filename {string} ファイル名(拡張子なし) mimetype {string} MIME_TYPE saved_to {string} save()メソッドの保存先パス saved_as {string} save()メソッドの保存ファイル名 |
※型とかなんか間違ってたらごめんなさい。
左から連想ワード・型名・説明
$file[‘size’] とかでファイルサイズを取得できるわけです。
アップロードファイルのデータサイズ
configのmax_sizeでアップロードファイルの最大サイズを指定していますが。が。が!
ここで好きなように設定できるわけではありません。
若干面倒ですが、php.iniの設定もいじらないといけないです。
また別の機会にそれ書きます。→php.iniの設定はこちらです
例のコードはあくまで例なので・・・
ファイルの保存場所なんて、公開フォルダの直下ですよ。URL打ち込めば見れちゃいますよ
FuelPHP tips データベースのcreated_at、updated_atが不要、または変更したい
0マイグレーションで生成されるデータベースにはcreated_atフィールドとupdated_atフィールドが付加されます。これらが不要な場合は、–no-timestampオプションを設定します。
$ oil g model テーブル名 [フィールド1 [フィールド2] ...] --no-timestamp
–no-timestampオプションが設定されると、モデルのコードに$_observersが生成されません。
また、created_atやupdated_atのフィールド名を変更したい場合は、オプションで--created-at=フィールド名
、--updated-at=フィールド名
を設定します。
FuelPHP tips マイグレーションしたテーブル名がなんか変
0マイグレーションで作成されたテーブル名は自動的に’s’や’es’が付加されて複数形になります。このため、生成されたテーブルが意図しないテーブル名になってしまうことがあります。
また、mysqlではテーブル名に’_’を使用できますが、FuelPHPは’_’をディレクトリ区切りと解釈するので、<code>$ oil r migrate</code>が失敗します(PDOドライバではダメでした。mysqliドライバでは大丈夫なようです)。
そのうえ、テーブル名を’user_account’などとした場合、fuel/app/classes/model/userディレクトリにaccount.phpが生成され、クラス名がModel_Accountとなってしまいます。
テーブル名の命名には、末尾が’s’で終わらないようにする、’_’を使わない、などの注意が必要です。
oil エラー”This is not a valid Fuel installation so Oil is a bit lost.”
0$ curl get.fuelphp.com/oil | sh
でFuelPHPを自動インストールして、その後oilコマンドを実行しようとすると、
This is not a valid Fuel installation so Oil is a bit lost.
とエラーを吐いて実行できないことがあります。
/usr/bin/oilをcatしてみると、./oilが存在するとき$ php oil
を実行し、./oilが無く且つcreate以外なら常に上記のエラーを表示して終了するようになっています。
つまり、/usr/bin/oilは、
$ php oil
とやらずに$ oil
で実行できるようにするラッパ- これ自体はcreateしかできない
よく考えれば判ることなんですが、自動インストールとは$ oil create
を実行することであり、/usr/bin/oilはインストーラのインストーラみたいな物なんですね。
ということで、まずはとにかく$ oil create dir
を実行して、その後oilコマンドを実行するときはcreateしたディレクトリで行いましょう。