【改訂版】本当は危ないCodeIgniter4の自動ルーティング
【警告】 自動ルーティングは大変危険なので無効に設定を変更するか、CodeIgniter 4.2から追加された新しい「自動ルーティング(改善)」を使うことを強く推奨します。 【警告】
(2022-04-26 追記) 「 浅いモデリングによる脆弱性、あるいはCodeIgniter4の自動ルーティングは何が問題だったのか?」も参照してください。
CodeIgniter4のルーティングはデフォルトではCodeIgniter3と同じくコントローラとメソッド名により、自動的にルーティングされます。 ルート設定を手動で行う必要がないため、非常に便利です。
しかし、コントローラフィルター機能が追加されたため、コントローラのロジックから一部がフィルターに分離されるようになりました。 例えば、認証済みかどうかの判定をフィルターに移すことができます。
この分離が新たなリスクを生み出しました。
今日は、この自動ルーティングの危険性について再度解説します。
動作確認環境
- CodeIgniter 4.2.0-dev (f801829)
- PHP 8.0.15
- PHPビルトインサーバー
- macOS 10.15.7
準備
動作を確認するための、テストコードを作成します。
Blogコントローラの作成
まず、コントローラを作成します。
app/Controllers/Blog.php
<?php
namespace App\Controllers;
class Blog extends BaseController
{
public function index()
{
echo __METHOD__ . '<br>';
}
}
http://localhost:8080/blog にアクセスすると、以下のようにメソッド名が表示されます。
App\Controllers\Blog::index
Authフィルターの作成
認証済みかどうかをチェックするためのフィルターを作成します。
app/Filters/Auth.php
<?php
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
class Auth implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
echo __METHOD__ . '<br>';
}
public function after(
RequestInterface $request,
ResponseInterface $response,
$arguments = null
) {}
}
ロジックはまだありませんが、beforeフィルターが適用されると、メソッド名が表示されます。
app/Config/Filters.php によるフィルター設定
フィルターの設定
それでは、http://localhost:8080/blog にアクセスされた場合に、Authフィルターが適用されるように設定します。
--- a/app/Config/Filters.php
+++ b/app/Config/Filters.php
@@ -2,6 +2,7 @@
namespace Config;
+use App\Filters\Auth;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Filters\CSRF;
use CodeIgniter\Filters\DebugToolbar;
@@ -23,6 +24,7 @@ class Filters extends BaseConfig
'honeypot' => Honeypot::class,
'invalidchars' => InvalidChars::class,
'secureheaders' => SecureHeaders::class,
+ 'auth' => Auth::class,
];
/**
@@ -64,5 +66,7 @@ class Filters extends BaseConfig
*
* @var array
*/
- public $filters = [];
+ public $filters = [
+ 'auth' => ['before' => ['blog']]
+ ];
}
動作確認
http://localhost:8080/blog にアクセスすると、以下のように実行されたメソッド名が表示されます。
App\Filters\Auth::before
App\Controllers\Blog::index
Authフィルターが適用されていることがわかります。OKですね。
フィルターの回避
さて、上記のコードにはフィルターを回避できる脆弱性があります。
その脆弱性を利用すると、フィルターを回避でき、以下の出力を得ることができます。
App\Controllers\Blog::index
これは、まずいですね。認証機能を回避できてしまうことになります。
フィルター設定の確認
CodeIgniter 4.2.0 のリリースにはまだしばらく時間がかかると思いますが、4.2.0 から、spark routes
コマンドで全てのルートとフィルターを確認できるようになります。
spark routes
コマンドを実行して確認してみましょう。
$ php spark routes
CodeIgniter v4.1.8 Command Line Tool - Server Time: 2022-02-07 20:27:59 UTC+09:00
+--------+------------------+------------------------------------------+----------------+---------------+
| Method | Route | Handler | Before Filters | After Filters |
+--------+------------------+------------------------------------------+----------------+---------------+
| GET | / | \App\Controllers\Home::index | | toolbar |
| CLI | ci(.*) | \CodeIgniter\CLI\CommandRunner::index/$1 | | |
| auto | blog | \App\Controllers\Blog::index | auth | toolbar |
| auto | blog/index[/...] | \App\Controllers\Blog::index | | toolbar |
| auto | / | \App\Controllers\Home::index | | toolbar |
| auto | home | \App\Controllers\Home::index | | toolbar |
| auto | home/index[/...] | \App\Controllers\Home::index | | toolbar |
+--------+------------------+------------------------------------------+----------------+---------------+
Method auto
は自動ルーティングのルートです。
これで一目瞭然ですが、auth
フィルターが適用されるルートは blog
だけです。blog/index[/...]
にはフィルターが適用されません。
正しいフィルター設定
正しいフィルター設定は以下になります。これで、フィルターを回避されません。
app/Config/Filters.php
public $filters = [
'auth' => ['before' => ['blog*']]
];
または、
public $filters = [
'auth' => ['before' => ['blog', 'blog/*']]
];
仮に、以下のようにURIを追加しても、まだ脆弱なので注意してください。
public $filters = [
'auth' => ['before' => ['blog', 'blog/index']]
];
app/Config/Filters.php
でのフィルター設定では、URIの指定には必ず最後にワイルドカード *
を含める必要があります。
正しいフィルター設定をした後にルートを確認しておきましょう。
$ php spark routes
CodeIgniter v4.1.8 Command Line Tool - Server Time: 2022-02-07 20:33:30 UTC+09:00
+--------+------------------+------------------------------------------+----------------+---------------+
| Method | Route | Handler | Before Filters | After Filters |
+--------+------------------+------------------------------------------+----------------+---------------+
| GET | / | \App\Controllers\Home::index | | toolbar |
| CLI | ci(.*) | \CodeIgniter\CLI\CommandRunner::index/$1 | | |
| auto | blog | \App\Controllers\Blog::index | auth | toolbar |
| auto | blog/index[/...] | \App\Controllers\Blog::index | auth | toolbar |
| auto | / | \App\Controllers\Home::index | | toolbar |
| auto | home | \App\Controllers\Home::index | | toolbar |
| auto | home/index[/...] | \App\Controllers\Home::index | | toolbar |
+--------+------------------+------------------------------------------+----------------+---------------+
blog
、blog/index[/...]
共に auth
フィルターが適用されるようになっています。
app/Config/Routes.php によるフィルター設定
app/Config/Filters.php
でのフィルター設定では設定ミスが起こりうるなら、
app/Config/Routes.php
でルートを指定してフィルター設定する方がいいでしょうか?
今度は、そのようにしてみましょう。app/Config/Filters.php
の $filters
は空の配列に戻します。
フィルターの設定
app/Config/Routes.php
でルートを追加し、フィルターを指定します。
--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -33,6 +33,8 @@ $routes->setAutoRoute(true);
// route since we don't have to scan directories.
$routes->get('/', 'Home::index');
+$routes->get('blog', 'Blog::index', ['filter' => 'auth']);
+
/*
* --------------------------------------------------------------------
* Additional Routing
これで、http://localhost:8080/blog に対して auth
フィルターが適用されます。
動作確認
http://localhost:8080/blog にアクセスすると、以下のように実行されたメソッド名が表示されます。
App\Filters\Auth::before
App\Controllers\Blog::index
Authフィルターが適用されていることがわかります。OKですね。
フィルターの回避
しかし、上記の設定でもやはりフィルターを回避できる脆弱性があります。
その脆弱性を利用すると、以下のようにフィルターを回避でき、以下の出力を得ることができます。
App\Controllers\Blog::index
フィルター設定の確認
spark routes
コマンドを実行して確認してみましょう。
$ php spark routes
CodeIgniter v4.1.8 Command Line Tool - Server Time: 2022-02-07 20:37:49 UTC+09:00
+--------+------------------+------------------------------------------+----------------+---------------+
| Method | Route | Handler | Before Filters | After Filters |
+--------+------------------+------------------------------------------+----------------+---------------+
| GET | / | \App\Controllers\Home::index | | toolbar |
| GET | blog | \App\Controllers\Blog::index | auth | auth toolbar |
| CLI | ci(.*) | \CodeIgniter\CLI\CommandRunner::index/$1 | | |
| auto | blog | \App\Controllers\Blog::index | | toolbar |
| auto | blog/index[/...] | \App\Controllers\Blog::index | | toolbar |
| auto | / | \App\Controllers\Home::index | | toolbar |
| auto | home | \App\Controllers\Home::index | | toolbar |
| auto | home/index[/...] | \App\Controllers\Home::index | | toolbar |
+--------+------------------+------------------------------------------+----------------+---------------+
auto
の blog
と blog/index[/...]
にフィルターが適用されていません。
正しい設定
はい、もうおわかりですね。これは自動ルーティングが有効だからです。
正しい設定方法は、自動ルーティングをオフにしてからルートを追加することです。
--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -21,7 +21,7 @@ $routes->setDefaultController('Home');
$routes->setDefaultMethod('index');
$routes->setTranslateURIDashes(false);
$routes->set404Override();
-$routes->setAutoRoute(true);
+$routes->setAutoRoute(false);
/*
* --------------------------------------------------------------------
これで、blog
へのルートしかなくなり、このルートにフィルターが指定されます。
$ php spark routes
CodeIgniter v4.1.8 Command Line Tool - Server Time: 2022-02-07 20:44:51 UTC+09:00
+--------+--------+------------------------------------------+----------------+---------------+
| Method | Route | Handler | Before Filters | After Filters |
+--------+--------+------------------------------------------+----------------+---------------+
| GET | / | \App\Controllers\Home::index | | toolbar |
| GET | blog | \App\Controllers\Blog::index | auth | auth toolbar |
| CLI | ci(.*) | \CodeIgniter\CLI\CommandRunner::index/$1 | | |
+--------+--------+------------------------------------------+----------------+---------------+
PHP8のアトリビュートでのルート設定
手動でルートを設定する場合、設定ファイルとコントローラが別々のため、ルートを設定したかどうかわかりづらいです。
CodeIgniter4 Attribute Routes を使うと、以下のようにコントローラにPHP8のアトリビュートを記載することで、ルートを設定することが可能です。
use Kenjis\CI4\AttributeRoutes\Route;
class SomeController extends BaseController
{
#[Route('path', methods: ['get'])]
public function index()
{
...
}
}
具体的な使用例は 『CodeIgniter徹底入門』のサンプルアプリケーション (CodeIgniter v4.1版) を参照してください。
まとめ
ルーティング&フィルター設定で最も安全な方法は以下です。
- 自動ルーティングは危険なのでオフに設定する
- ルートはすべて手動でHTTPメソッド別に設定する
- フィルターはルートに対して指定する
また、
app/Config/Filters.php
でのフィルター設定では、URIの指定には必ず最後に*
を含める
参考
Date: 2022/02/08