Phalcon で安全なアプリを書くために気をつけることメモ

 

とりあえずメモとして殴り書きです．

# $_GET, $_POST について

DI にある request の getQuery() や getPost() の第二引き数に int や trim などをつけると余計なものを落としてくれます．第一引き数を null にすると全部取ってこれますが フィルタは動いてくれないので注意しましょう．（これについてはやり方がまだよくわかってない）

```php
$hoge = $request->getQuery('hoge', ['trim', 'int']);
```

第二引き数のフィルタは DI の filter が使われているので自作フィルタを使いたい場合は DI に登録しましょう．


ちなみに [ここ](http://docs.phalconphp.com/ja/latest/api/Phalcon_Http_Request.html) には $request->get() は `$_REQUEST` が返ってくるとかいてますが `$_COOKIE` は含まれていないので注意しましょう．

また API には３つまでしか引き数が書かれていませんがコードを見る限りだと 第４，第５の引き数として not_allow_empty, norecursive が指定できるようすです．詳しくは見てないです．


# ヘッダの付け方

Phalcon でヘッダを付ける場合は DI に設定されている response にヘッダを設定します．
Phalcon では response オブジェクトは DI に格納されているので Injectable を実装しているクラス内では $this->response でアクセスできます．
アクセスした response に対して setHeader() を呼び出しましょう． あるいは  DI 登録時に設定してしまえばいいと思います．

```php
$di->set('response', function() {
    $response = new \Phalcon\Http\Response();
    $response->setHeader('X-Frame-Options', 'SAMEORIGIN');
    $response->setHeader('X-Content-Type-Options', 'nosniff');
    return $response;
}, true)
```

前にも書いたけど上に書いたものと情報量はさほど変わらない．
http://qiita.com/nise_nabe/items/6984a0d8e1f1cfdf41b6

# セッションについて

ログイン・ログアウト時には session_regenerate_id(true)  をやったほうがよいと思いますが Phalcon\Session\AdapterInterface を実装しているクラスの destroy() を含む Phalcon 側のメソッドではそれを設定したり呼び出す方法がありません．
なので自分で session_regenerate_id(true) を呼び出す必要があります．注意しましょう．


アプリ側でセッションIDの Cookie に対して Secure 属性や HttpOnly 属性を付けたい場合は Phalcon 側のメソッドで設定する方法はありません． php.ini で設定したり自分で session_set_cookie_params() を呼び出しましょう．

```php
$di->set('session', function() {
    $session = new Phalcon\Session\Adapter\Files();
    // 必要な値とか
    session_set_cookie_params($lifetime, $path, $domain, $secure, $httponly);
    $session->start();
    return $session;
}, true);
```

下記イシューは参考になると思います．
https://github.com/phalcon/cphalcon/issues/277


また `$session->destroy()` をしてもそのリクエスト中では `$session->get()`  によって 値が取得可能なようです． `$session->destroy()` の前後で セッションデータを扱う場合は注意しましょう．

# CSRF 対策について

DI に設定されている security の getToken() などを活用しましょう．

前に書いたのであとは割愛 http://qiita.com/nise_nabe/items/0ce9128c71a8e5da1fd7


# HTML エスケープ

DI に設定されている escaper を活用しましょう．

明示的にエスケープしたい場合は下記のように使えます．

```php
/** @var \Phalcon\EscaperInterface */
$escaper = $this->di->get('escaper');
$escapedString =  $escaper->escapeHtml('<script>alert("TEST");</script>')
```

あとは下記ページを見ましょう．
http://docs.phalconphp.com/ja/latest/reference/escaper.html

volt 内では e または escape フィルタが使えます．これは volt -> php のコンパイル時に上記の escapeHtml() でラップされます．

```
{{ '<script>alert("TEST");</script>'|e }}
```

毎度毎度フィルタ設定するのも面倒なので勝手にエスケープされるようにはできます http://docs.phalconphp.com/ja/latest/reference/volt.html#id25

```
{% autoescape true %}
  {{ '<script>alert("TEST");</script>' }}
{% endautoescape %}
```

ただし autoescape 時は text_field() など html を出力する前提のヘルパについてもエスケープしてしまうためフォームを扱うページでは使い物になりません．
フォームのページでは各値について頑張ってフィルタを追加しましょう． Volt を頑張って拡張すればなんとかできなくもないけどまた今度．


Volt のヘルパなどは普通に書いてるとわりとやらかす可能性が高いです．
Phalcon\Tag には setAutoescape() というメソッドがあり，これにより $tag->setDefaults() などで設定したものが自動でエスケープされるのではと期待されます．
この setAutoescape() の効果としては Tag クラスによって生成される html タグの属性についてエスケープをする設定です． 
なので　setDefaults() などで設定した値が勝手にエスケープされるわけではないので注意しましょう．明示的にエスケープする必要があります．

```php
$this->tag->setDefaults([
    'mytext' => '<script>alert("TEST");</script>'
]);
```

```
{# アウト #}
{{ getValue('mytext') }}
{# 大丈夫 #}
{{ getValue('mytext')|e }}
```


また，それ以外，例えば link_to() による a タグで囲まれた中身などはエスケープされないので注意しましょう．
`<a><img /></a>` など 中に html をはさみたい場合はそのままでいいと思いますがたぶん普通に変数をそのまま指定している場合が多いと思います．
下記のような感じになります．html を使わない予定ならばフィルタを活用しましょう．

```twig
{# アウト #}
{{ link_to('http://google.co.jp', <script>alert("TEST");</script>') }}
{# アウト #}
{{ link_to('http://google.co.jp', 'text':'<script>alert("TEST");</script>') }}
{# 大丈夫 #}
{{ link_to('http://google.co.jp', 'text':'<script>alert("TEST");</script>'|e) }}
{# $tag->setAutoescape(true) なら大丈夫 #}
{{ link_to('http://google.co.jp', 'value':'"><script>alert("TEST");</script>') }}
```