🔥

CodeIgniter4 コアの TypeError 500 をカスタム例外ハンドラで掴み 400 にした話

2024/06/17に公開

脆弱性診断を受けた際、
リクエスト改竄に対する適切な例外処理が実装されていないと
(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 でエラー画面が表示される。

参考

セキュリティ監査で文句を言われないHTTPステータスコードの使い分け

Discussion