FuelPHP 1.xのパッケージをComposerでインストールする

FuelPHP 1.xはComposerと統合されていますので、FuelPHPのパッケージをComposerで管理することも可能です。

というか、Composerで管理した方が何かと便利でしょう。

FuelPHPのパッケージがPackagistに登録されている場合

あまり多くはないですが、FuelPHPのパッケージがPackagistに登録されていれば、普通のComposerのパッケージと同様にcomposer.jsonrequireにパッケージ名を記載するだけでインストールできます。

例えば、このようにインストールできます。

まず、FuelPHPをComposerでインストールします。

$ composer create-project fuel/fuel:dev-1.7/master project --prefer-dist

composer requireコマンドでFuelPHPのパッケージ(hosopy/fuel-jobqueue)をインストールします。

$ cd project
$ composer require hosopy/fuel-jobqueue:dev-master
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)                          
  - Installing pda/pheanstalk (v3.0.2)
    Downloading: 100%         

  - Installing symfony/process (v2.3.0)
    Downloading: 100%         

  - Installing hosopy/fuel-jobqueue (dev-master 36a6843)
    Cloning 36a6843b12fdfa97397d72f723e5f87132a1dc22

Writing lock file
Generating autoload files

これでfuel/packages以下にインストールされました。

$ ls -l fuel/packages/
total 0
drwxr-xr-x  12 kenji  staff  408  6 30 06:10 auth
drwxr-xr-x   7 kenji  staff  238  6 30 06:10 email
drwxr-xr-x  12 kenji  staff  408  6 30 06:23 fuel-jobqueue
drwxr-xr-x  10 kenji  staff  340  6 30 06:10 oil
drwxr-xr-x   7 kenji  staff  238  6 30 06:10 orm
drwxr-xr-x   8 kenji  staff  272  6 30 06:10 parser

ここでのcomposer.jsonの変更点は以下のみです。

$ git diff
diff --git a/composer.json b/composer.json
index 3fa09b0..26833d1 100644
--- a/composer.json
+++ b/composer.json
@@ -25,7 +25,8 @@
         "fuel/parser": "dev-1.7/master",
         "fuelphp/upload": "2.0.2",
         "monolog/monolog": "1.5.*",
-        "michelf/php-markdown": "1.4.0"
+        "michelf/php-markdown": "1.4.0",
+        "hosopy/fuel-jobqueue": "dev-master"
     },
     "suggest": {
         "dwoo/dwoo" : "Allow Dwoo templating with the Parser package",

FuelPHPのパッケージがPackagistに登録されていない場合

Packagistに登録されていない場合は、composer.jsonrepositoriesに以下のようにパッケージの情報を記載した上でインストールする必要があります。

ここでは、Benni-chan/FuelPHP-Pushnotificationsを例にします。

--- a/composer.json
+++ b/composer.json
@@ -11,7 +11,20 @@
         { "type": "vcs", "url": "https://github.com/fuel/email" },
         { "type": "vcs", "url": "https://github.com/fuel/oil" },
         { "type": "vcs", "url": "https://github.com/fuel/orm" },
-        { "type": "vcs", "url": "https://github.com/fuel/parser" }
+        { "type": "vcs", "url": "https://github.com/fuel/parser" },
+        {
+            "type": "package",
+            "package": {
+                "name": "benni-chan/pushnotifications",
+                "type": "fuel-package",
+                "version": "1.0.0",
+                "source": {
+                    "url": "https://github.com/Benni-chan/FuelPHP-Pushnotifications.git",
+                    "type": "git",
+                    "reference": "master"
+                }
+            }
+        }
     ],
     "require": {
         "php": ">=5.3.3",
@@ -26,7 +39,8 @@
         "fuelphp/upload": "2.0.2",
         "monolog/monolog": "1.5.*",
         "michelf/php-markdown": "1.4.0",
-        "hosopy/fuel-jobqueue": "dev-master"
+        "hosopy/fuel-jobqueue": "dev-master",
+        "benni-chan/pushnotifications": "1.0.*"
     },
     "suggest": {
         "dwoo/dwoo" : "Allow Dwoo templating with the Parser package",

ここで、repositories全体は以下のようになっています。

    "repositories": [
        { "type": "vcs", "url": "https://github.com/fuel/docs" },
        { "type": "vcs", "url": "https://github.com/fuel/core" },
        { "type": "vcs", "url": "https://github.com/fuel/auth" },
        { "type": "vcs", "url": "https://github.com/fuel/email" },
        { "type": "vcs", "url": "https://github.com/fuel/oil" },
        { "type": "vcs", "url": "https://github.com/fuel/orm" },
        { "type": "vcs", "url": "https://github.com/fuel/parser" },
        {
            "type": "package",
            "package": {
                "name": "benni-chan/pushnotifications",
                "type": "fuel-package",
                "version": "1.0.0",
                "source": {
                    "url": "https://github.com/Benni-chan/FuelPHP-Pushnotifications.git",
                    "type": "git",
                    "reference": "master"
                }
            }
        }
    ],

ここでは、パッケージのリポジトリにcomposer.jsonが存在しないため、"type": "package"を指定しパッケージの情報を記述します。

nameversionは適当に指定します。パッケージをアップデートする場合は、versionを上げる必要があります。

"type": "fuel-package"はFuelPHPのパッケージであることの指定です。これがないとfuel/packages以下にインストールされません。

なお、インストール先がデフォルトのvendorから変更されるのは、composer/installerの機能です。require"composer/installers": "~1.0"のようにcomposer/installerが必ず必要です。FuelPHPのデフォルトのcomposer.jsonに記載されているので通常は意識することもないですが。

もし、パッケージのリポジトリにcomposer.jsonが存在する場合は、リポジトリの指定はFuelPHPの標準のパッケージのように以下の形式で済みます。

{ "type": "vcs", "url": "https://github.com/fuel/auth" }

composer.jsonの設定が済んだら、インストールします。

$ composer update benni-chan/pushnotifications
Loading composer repositories with package information
Updating dependencies (including require-dev)                          
  - Installing benni-chan/pushnotifications (1.0.0)
    Cloning master

Writing lock file
Generating autoload files

これでfuel/packages以下にインストールされました。

$ ls -l fuel/packages/
total 0
drwxr-xr-x  12 kenji  staff  408  6 30 06:10 auth
drwxr-xr-x   7 kenji  staff  238  6 30 06:10 email
drwxr-xr-x  12 kenji  staff  408  6 30 06:23 fuel-jobqueue
drwxr-xr-x  10 kenji  staff  340  6 30 06:10 oil
drwxr-xr-x   7 kenji  staff  238  6 30 06:10 orm
drwxr-xr-x   8 kenji  staff  272  6 30 06:10 parser
drwxr-xr-x   7 kenji  staff  238  6 30 06:56 pushnotifications

関連

Tags: fuelphp, composer

tokenを聞かれFuelPHP 1.7.3がうまくインストールできない場合

(2016-03-02 追記) 「2016年FuelPHP 1.xのインストール方法のベストプラクティス」を書きました。

FuelPHP 1.7.3をインストールしようとすると以下のようにトークンを聞かれることがあります。

Could not fetch https://api.github.com/repos/fuel/auth/git/refs/heads?per_page=100, please create a GitHub OAuth token to go over the API rate limit
Head to https://github.com/settings/tokens/new?scopes=repo&description=Composer+on+<servername>+2015-06-26+0933
to retrieve a token. It will be stored in "/home/<username>/.composer/auth.json" for future use by Composer.
Token (hidden): 

はじめてこのようなメッセージを見ると、なんでインストールするのにトークンが必要なの?と疑問に思うことでしょう。

この理由は、GitHub APIのレート制限に引っかかり、GitHub APIに匿名でアクセスできなくなったためです。

つまり、FuelPHPがどうのという問題ではなく、GitHubの問題です。

GitHub APIのレート制限?

GitHub APIなんか知らないよ。俺はだたFuelPHPをインストールしたいだけなんだ、と思う人もいることと思います。

これは、ComposerがダウンロードのためにGitHub APIを使っているということです。

Composerのドキュメントにもこのことについて記述されています。

つまり、FuelPHPだからこうなるというものではなく、GitHub APIのレート制限なのでいつでもどんなパッケージでも起こりうることです。

匿名でのアクセスは現在1時間に60回です。これはIPアドレス単位で制限されます。

レート制限の状態は以下のようにGitHub APIにリクエストを送ればわかります。

$ curl -i https://api.github.com/users/whatever

例えば、以下のような結果が返ります。

HTTP/1.1 403 Forbidden
Server: GitHub.com
Date: Fri, 26 Jun 2015 00:34:18 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 257
Status: 403 Forbidden
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1435281388
X-GitHub-Media-Type: github.v3
X-XSS-Protection: 1; mode=block
X-Frame-Options: deny
Content-Security-Policy: default-src 'none'
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
Access-Control-Allow-Origin: *
X-GitHub-Request-Id: DB5EF43C:6D1D:E6F2EE8:558C9E0A
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
X-Content-Type-Options: nosniff

{
  "message": "API rate limit exceeded for XXX.XXX.XXX.XXX. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)",
  "documentation_url": "https://developer.github.com/v3/#rate-limiting"
}

ここで、以下のヘッダから制限は、1時間に60回で残り0回、この制限がリセットされる時間はUnix時間で1435281388であることがわかります。

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1435281388

対処方法

対処方法は、メッセージに書かれているのですが、GitHubのOAuthトークンを作成しComposerに設定することです。

please create a GitHub OAuth token to go over the API rate limit

つまり、匿名アクセスをやめてGitHubに身元を明らかにするということです。

トークンの作成はメッセージに表示されるURLにアクセスすればすぐにできます。

https://github.com/settings/tokens/new?scopes=repo&description=Composer+on+<servername>+2015-06-26+0933

GitHubアカウント持ってないんだけど?

しかし、トークンを作成するにはGitHubアカウントが必要です。GitHubアカウントを持っていない場合は、GitHubアカウントを作成するか、レート制限がリセットされるまで待つしかありません。

制限がリセットされるまで待つのはあんまりなので、Zipファイルからインストールするという方法もあるのですが、あいにく現在の公式のZipファイルはcomposer updateするとエラーになるという問題があります(それ以外の動作に問題は生じませんが)。

そこで、修正したZipファイルを作成しました。

(16:25 追記) 公式のZipファイルが更新され、上記の問題は修正されましたので、以下のZipを使う必要はなくなりました。

  • http://fuelphp1st.com/downloads/fuelphp-1.7.3-fixed.zip

関連

Tags: fuelphp, composer

『CodeIgniter徹底入門』のサンプルコードにXSS脆弱性が見つかった話

『CodeIgniter徹底入門』に掲載されている「簡易ショッピングサイト」のコードにXSS脆弱性が発見されました。発見したのは私です。なので、それでちゃらにしてください。

XSS脆弱性の内容

エスケープ漏れによる反射型のXSSです。

該当する箇所は以下になります。

<p class="coment"><?=$total_item?></p>

https://github.com/kenjis/codeigniter-tettei-apps/blob/62b2a4b05fcad2ab4556b33acd6beca0dc142cfe/system/application/views/shop_search.php#L5

修正パッチ

単純なエスケープ漏れなので、修正はエスケープすればOKです。

--- a/7-9/system/application/views/shop_search.php
+++ b/7-9/system/application/views/shop_search.php
@@ -2,7 +2,7 @@

 <?=$pagination?>

-<p class="coment"><?=$total_item?></p>
+<p class="coment"><?=form_prep($total_item);?></p>

 <?php foreach($list as $row): ?>
 <a href="<?=base_url();?>shop/product/<?=$row->id?>">

form_prep()関数は古いCodeIgniterでのHTMLエスケープのためのヘルパー関数です。現在ではhtml_escape()関数を使ってください。

何故脆弱性が作り込まれたのか?

→エスケープが漏れたから。

何故エスケープが漏れたのか?

→必要性が見落とされたため。

何故必要性が見落とされたのか?

→必要性の判断を誤った(必要なのに不要と判断した、あるいは、見逃した)ため。

何故必要性の判断を誤ったのか?

→必要性の判断が難しい、手間がかかるから。

何故必要性の判断が難しいのか?

→エスケープが必要かどうかをコードを遡って確認しないとわからないから。

とまあ、こんな感じでそもそも必要な箇所をエスケープするというアプローチが間違っていることがわかります。

何故必要な箇所をエスケープするというアプローチだったのか?

→CodeIgniterの書籍なので標準の機能をつかうべきと考えたが、CodeIgniterに自動エスケープの機能がなかったため。あるいは時間がなかったため。

ともかく、実際にエスケープ漏れが発生して脆弱性のあるコードが出荷されてしまったという事実を重く受け止める必要があります。

どうすればよかったのか?

エスケープ漏れをなくすには、エスケープが必要かどうかの判断をなくす、あるいは、判断を簡単にすることです。

また、エスケープしていない箇所を簡単に探せるようにすることも漏れを発見し防ぐためには役立つでしょう。

例えば、以下のような対策が考えられます。

  1. HTMLでの変数の出力をなくす
  2. HTMLでの変数の出力時にはすべて自動的にエスケープする

1.はHTMLに変数を埋め込むことを規約として禁止し、変数はAjaxでJSONで渡すとかです。

2.は

  • テンプレートエンジンを使いデフォルトでエスケープする
  • echoなど生で出力する命令の使用を規約として禁止して、専用の自動でエスケープする出力関数を作成し必ずそれを使い出力する

という方法が考えられます(ただし残存リスクあり)。

1.は制約としてきついので2.で対応する方が柔軟かと思います。CodeIgniterであれば例えばTwigを使うという方法があります。

ただし、2.の場合は、コンテクストに応じたエスケープをしないと脆弱性になるというリスクは残ります。

まとめ

  • HTMLに変数を出力する際に、手動でエスケープ関数を書くというコーディングは絶対に止めましょう。1か所忘れたらXSS脆弱性になる可能性があります。少なくともデフォルトでエスケープされるようにすべきです。

関連

Tags: xss, security, codeigniter, book