🔥
CodeIgniter4 コアの TypeError 500 をカスタム例外ハンドラで掴み 400 にした話
脆弱性診断を受けた際、
リクエスト改竄に対する適切な例外処理が実装されていないと
(500 Internal Server Error はダメ)
指摘されたのでやむを得ず強引に対応した記録。
実行環境
- PHP 8.2.12
- CodeIgniter 4.5.1
サンプル
<?php echo form_open('/'); ?>
<button type="submit">Submit</button>
<?php echo form_close(); ?>
↓
<form action="http://localhost:8080/" method="post" accept-charset="utf-8">
<input type="hidden" name="csrf_test_name" value="xxxxxxxxxx">
<button type="submit">Submit</button>
</form>
疑似的に攻撃を再現
<form action="http://localhost:8080/" method="post" accept-charset="utf-8">
- <input type="hidden" name="csrf_test_name" value="xxxxxxxxxx">
+ <input type="hidden" name="csrf_test_name[]" value="xxxxxxxxxx">
<button type="submit">Submit</button>
</form>
CSRF トークンの name 属性を変更して、配列型で POST する。
HTTP ステータスコード 500 でエラー画面が表示される。
development で確認すると CodeIgniter コア部分の実装で TypeError が発生していることが
わかる。
対応内容
Error Handling — CodeIgniter 4.5.1 documentation
Custom Exception Handlers を参考に TypeError を 400 Bad Request として扱う以下を実装。
app\Libraries\TypeErrorHandler.php
<?php
namespace App\Libraries;
use CodeIgniter\Debug\BaseExceptionHandler;
use CodeIgniter\Debug\ExceptionHandlerInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Throwable;
class TypeErrorHandler extends BaseExceptionHandler implements ExceptionHandlerInterface
{
protected ?string $viewFile = APPPATH . 'Views/errors/html/production.php';
public function handle(
Throwable $exception,
RequestInterface $request,
ResponseInterface $response,
int $statusCode,
int $exitCode
): void {
$statusCode = 400;
$response->setStatusCode($statusCode);
if (!headers_sent()) {
header(
sprintf(
'HTTP/%s %s %s',
$request->getProtocolVersion(),
$response->getStatusCode(),
$response->getReasonPhrase()
),
true,
$statusCode
);
}
$this->render($exception, $statusCode, $this->viewFile);
exit($exitCode);
}
}
app\Config\Exceptions.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Debug\ExceptionHandler;
use CodeIgniter\Debug\ExceptionHandlerInterface;
use Psr\Log\LogLevel;
use Throwable;
class Exceptions extends BaseConfig
{
// ...
public function handler(int $statusCode, Throwable $exception): ExceptionHandlerInterface
{
+ if (ENVIRONMENT === 'production' && $exception instanceof \TypeError) {
+ return new \App\Libraries\TypeErrorHandler($this);
+ }
return new ExceptionHandler($this);
}
}
HTTP ステータスコード 400 でエラー画面が表示される。
Discussion