2014年5月8日木曜日

FuelPHPでセキュアな設定をする

FuelPHPをお仕事で使う方も増えてきたと思います。
そんな中で気になるのはやはりセキュリティ。
ということで今日はFuelPHPでセキュアな状態にするための設定を書きます。
まず

@kenji_sさんの下記のブログが非常に分かりやすく書かれています。
今日はこれ以外の下記の項目について書きます。

  1. DBにセッションを保存
  2. 変更推奨のセッション情報
  3. https時にCookieにsecure属性を付与

1.DBにセッションを保存

defaultはセッションをクッキーに保存しています。
これをDBに変える情報は公式ドキュメントにあります。

FuelPHP公式ドキュメント 1.7

DB以外にも

  • File
  • Memcached
  • Redis

などが選べます。
DBを使う際にMySQLの方はいいのですがPostgreSQLを使う方は

#reate the sessions table
$ php oil r session:create

が動きません。
なのでCREATE文と設定方法をご紹介します。

まず

fuel/core/config/session.php



fuel/app/config/session.php

にコピーします。
次に fuel/app/config/session.php のdriverの項目でdbを指定します。

'driver'  => 'db',
次にdbにセッション専用のテーブルを作成します。
下記SQLで作成できます。

--PostgreSQL用
CREATE TABLE sessions
(
session_id character varying(40) NOT NULL,
previous_id character varying(40) NOT NULL,
user_agent text NOT NULL,
ip_hash character varying(32) NOT NULL DEFAULT ''::character varying,
created integer NOT NULL DEFAULT 0,
updated integer NOT NULL DEFAULT 0,
payload text NOT NULL,
CONSTRAINT sessions_pkey PRIMARY KEY (session_id),
CONSTRAINT sessions_payload_key UNIQUE (payload)
)
WITH (
OIDS=FALSE
);
ALTER TABLE sessions
OWNER TO ユーザ名;

これでセッションをDBに保存するようになります。

2.セッション時間の変更

セッション利用する際に変更推奨の項目です。
1.でcopyしてきた

fuel/app/config/session.php

の項目に

//セッションIDを保存するCookie名
cookie_name

があります。
こちらはdefaultの名前ではFuelPHPのどのdriverを使ってるかがわかります。
セキュアな環境を求められる際は変更した方が良いと思います。
似たような設定にCSRF対策時に利用するkey名があります。
こちらは

app/config/config.php

のSecurityの項目の

csrf_token_key

の項目で変更できます。

それ以外にもsession.phpには

  • expiration_time
  • rotation_time
  • match_ip
  • match_ua

などセキュアにセッションを扱うための設定があります。
defaultはゆるく設定されていますので要件に合わせて設定を変更すると良いと思います。

3.https時にCookieにsecure属性を付与

httpsの際にCookieにsecure属性をつけていないと盗聴される可能性があります。

PHP と Web アプリケーションのセキュリティについてのメモ

Cookie の secure 属性


セキュリテイについては安定の徳丸さんの記事を御覧ください。

徳丸浩の日記

HTTPSを使ってもCookieの改変は防げないことを実験で試してみた


FuelPHPはdefaultでは付与していません。
ですが設定一つで対応できます。
設定箇所は

fuel/app/config/config.php

'cookie' => array()

を有効にし

'secure' => false,



'secure' => true,

と変更します。
これでsecure属性を付与してくれます。
ただし、secure属性は

  • https通信時のみ Cookie の内容を送信
  • http通信時には Cookie の内容を送らない

というものですのでhttpでアクセスした際はCookieの内容を送信しません。
ですのでhttp通信時はセッションの保持もCSRFも正常に動作しません。
(本版環境はhttpsだけど開発環境はhttpの場合などでハマるポイントですね)

以上の3点が本番リリース時に気にする項目だと思います。
FuelPHPとは関係ないですがヘッダー情報でphpとapacheのバージョン情報が分かります。
こちらはapacheのhttpd.confやphp.iniを修正する必要があります。

■apacheの場合
・対象File
CentOS:/etc/httpd/httpd.conf
ubuntu:/etc/apache2/conf-enabled/security.conf

・変更箇所
ServerTokens Prod
ServerSignature Off

■PHPの場合
・対象File
php.ini

・変更箇所
expose_php = Off

となります。
上記と合わせて設定をご検討していただけたらと思います。


ということでFuelPHPのconfigは色々と設定出来るので一読すると面白いと思いますよ!!

Symfony2でDoctrine2使う時によく使うEntityのアノテーション一覧とDoctrine2のコマンド一覧

Symfony2シリーズ第二弾です。
Doctrine2はアノテーションでEntityの関係や属性を指定出来るので便利です。
ただドキュメントが英語だったので自分用によく使うヤツをまとめて置いときます。

公式ドキュメント(アノテーション一覧)

/**
 * UNIQUE制約のIndexを作るとき
 * nameを指定しない場合はIDX_6E72A8C13B66675Bのように種類_ランダムの名前が付けられる
 * @ORM\Table(name="テーブル名", uniqueConstraints={@UniqueConstraint(name="キー名", columns={"カラム名"})})
 * 複数指定もできる
 * @ORM\Table(name="テーブル名", uniqueConstraints={@UniqueConstraint(columns={"カラム名", "カラム名"})})
 *
 * Indexを指定したい時
 * @ORM\Table(name="テーブル名", indexes={@index(columns={"カラム名"})})
 * 複数指定もできる
 * @ORM\Table(name="テーブル名", indexes={@index(columns={"カラム名", "カラム名"})})
 *
 **/
class テーブル名
{
    /**
     * ID
     *
     * @var integer
     * 
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * オートインクリメントの指定
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * ほげ(外部キー貼る側)
     * 外部制約(リレーションを指定するときは名前空間を指定すると補完が効く
     * @var \project\MyBundle\Entity\Hoge\Hoge
     * リレーションの指定
     * @ManyToOne(targetEntity="\project\MyBundle\Entity\Hoge\Hoge", inversedBy="hoge")
     * @JoinColumn(name="hoge_id", referencedColumnName="id")
     * */
    private $hoge;

    /**
     * ほげ(外部キー貼られる側)
     * 外部キー貼られる側も指定がいる
     * @var \Doctrine\Common\Collections\ArrayCollection
     * カスケードの動作等は貼られる側で指定する
     * @OneToMany(targetEntity="\project\MyBundle\Entity\Hoge\Hoge", mappedBy="hoge", cascade={"persist","remove"})
     * */
    private $hoge;
 
     /**
     * 登録日時
     *
     * @var \DateTime
     *
     * @Gedmo\Timestampable(on="create")
     * @ORM\Column(name="created_at", type="datetime")
     */
    private $createdAt;

    /**
     * 更新日時
     *
     * @var \DateTime
     *
     * @Gedmo\Timestampable(on="update")
     * @ORM\Column(name="updated_at", type="datetime")
     */
    private $updatedAt;
}

注意が必要なのはリレーションを定義するときは両方のEntityにアノテーションを記載する必要があります。
そしてSQL的にはカスケードの条件は外部キーを貼る側に記載しますがアノテーションは外部キーを貼られる側に記載します。
というよりも実際にカスケードの条件はDDLとしては付与されてません。
アノテーションでのカスケード処理はDoctrineが処理する際に行うだけで実際のDBのDDLには関与しないので注意が必要です。
同じようにdefaultやon="create"も同様です。
DDLとしてdefaultやtimestamp型が使われるわけではないのです。
このへんはDBとコードが不一致なので動作については注意が必要ですね。

それとDoctrine2のよく使うCUIコマンドです。

//DBの作成
php app/console doctrine:database:create

//エンティティの作成
php app/console doctrine:generate:entity --entity="MyBundle:Entity名" --fields="name:string(255) price:float description:text"

//ゲッター・セッターの作成
php app/console doctrine:generate:entities project名/MyBundle/Entity/エンティティ名

//全部のエンティティの作成
php app/console doctrine:generate:entities projectMyBundle

//テーブルの作成
php app/console doctrine:schema:update --force

//データの投入
php app/console doctrine:fixtures:load --env=test


Doctrine2でDBマイグレーションを管理すると環境の複製やリリース等が簡単になります。
またFuelPHP2.0でもDoctrineを採用するようです。
PHPフレームワークのDBのスタンダードになりつつあるのでDoctrine2を覚えておいて損はないと思います。
(NetBeansはDoctrine2対応していますしね!)


ということで今日のところは以上です。

Symfony2のTwigExtentionをからContainerやEntityManagerを呼び出す

Symfony2は重厚で大規模開発には非常に優秀だと思います。
そんなSymfony2ですがちょっと手の込んだ事を調べると日本語情報が少ない印象です。
英語の公式ドキュメントとstackoverflow.comを行ったり来たりした結果をメモとして残しておきます。

やりたいこと


  1. Symfony2でTwigExtentionを作る
  2. 作ったTwigExtentionのgetGlobals()からDBの呼び出しやサービスの呼び出しをする
  3. base.html.twigの変数にDBから取り出した値を使う。

この2.をするのに情報がなかったです。
1.については公式ドキュメントがありますのでそちらを参考にしてください。

カスタムTwig拡張の書き方


さて本題ですがTwigExtentionをサービスコンテナに登録しましょう。
その際に

# src/Acme/DemoBundle/Resources/config/services.yml
services:
    acme.twig.acme_extension:
        class: Acme\DemoBundle\Twig\AcmeExtension
        tags:
            - { name: twig.extension }

    arguments:
            em: "@doctrine.orm.entity_manager"
            container: "@service_container"

とargumentsとしてEntityManagerとContainerを渡します。
次にTwigExtentionクラスのコンストラクタに
public function __construct($em, $container)
    {
        $this->em = $em;
        $this->container = $container;
    }
と記載します。
あとはControllerと同様に各メソッドからDoctrine2のmodelを呼び出すだけです。
実際にbase.html.twigの初期値にDBから取り出したデータを使いたいときはgetGlobals()をオーバーライドします。
public function getGlobals()
    {

        $var = $this->container
                ->get('my_domain.hoge_repository')
                ->getHoge();
        return [
            'hoge' => $hoge,
            'fuga' => 'fuga',
        ];
    }
と言った感じです。
base.html.twigの拡張すればかなりDRYにすることが出来るます。
ただしTwigExtentionでDBを呼び出すと毎回呼ばれます。
ですので出来るだけ回数を減らす必要があると思います。

Symfony2シリーズ続くかわかりませんが現場からは以上です。