From 0f15f25280b0bcf3eafb776186bd9280257a9425 Mon Sep 17 00:00:00 2001 From: mscherer Date: Sat, 2 May 2026 15:50:10 +0200 Subject: [PATCH] Harden admin backend: SRI, GET-only display, IP input validation - templates/layout/captcha-admin.php:14-15,61: pin Bootstrap 5.3.3 CSS/JS and FontAwesome 6.5.2 CSS to SHA-384 SRI hashes, add crossorigin="anonymous" and referrerpolicy="no-referrer" to remove Referer leakage and protect against compromised CDN responses executing attacker-controlled JS in a privileged surface. - src/Controller/CaptchaController.php:56: enforce GET on display(); prevents cross-method overwrite of generated captcha state via a POST forged from same-origin contexts. - src/Controller/Admin/IpsController.php:56,88,104: validate the IP path argument with FILTER_VALIDATE_IP via a new assertValidIp() helper; throws BadRequestException on non-IP input. Stops arbitrary path strings reaching queries, cache keys and flash message placeholders (defense-in-depth, log poisoning). - Tests: assert MethodNotAllowedException on POST display, and BadRequestException on non-IP arguments to view/reset/clearRateLimit. --- src/Controller/Admin/IpsController.php | 28 +++++++++++++++-- src/Controller/CaptchaController.php | 2 ++ templates/layout/captcha-admin.php | 17 ++++++++-- .../Controller/Admin/IpsControllerTest.php | 31 +++++++++++++++++++ .../Controller/CaptchaControllerTest.php | 12 +++++++ 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/Controller/Admin/IpsController.php b/src/Controller/Admin/IpsController.php index 851adf4..9db6c95 100644 --- a/src/Controller/Admin/IpsController.php +++ b/src/Controller/Admin/IpsController.php @@ -5,6 +5,7 @@ use Cake\Cache\Cache; use Cake\Core\Configure; +use Cake\Http\Exception\BadRequestException; use Cake\I18n\DateTime; use Captcha\Cache\RateLimitKey; @@ -54,7 +55,7 @@ public function index(): void { * @return void */ public function view(?string $ip = null): void { - $ip = (string)$ip; + $ip = $this->assertValidIp($ip); $query = $this->Captchas->find() ->where(['ip' => $ip]) ->orderBy(['created' => 'DESC']); @@ -87,7 +88,7 @@ public function view(?string $ip = null): void { */ public function reset(?string $ip = null) { $this->request->allowMethod('post'); - $ip = (string)$ip; + $ip = $this->assertValidIp($ip); $count = $this->Captchas->reset($ip); @@ -103,7 +104,7 @@ public function reset(?string $ip = null) { */ public function clearRateLimit(?string $ip = null) { $this->request->allowMethod('post'); - $ip = (string)$ip; + $ip = $this->assertValidIp($ip); $rl = (array)Configure::read('Captcha.verifyRateLimit'); $cache = (string)($rl['cache'] ?? 'default'); @@ -172,6 +173,27 @@ protected function topIps(DateTime $since, ?bool $solved): array { return $out; } + /** + * Validate the IP path argument as a real IPv4/IPv6 address. + * + * Defense-in-depth against arbitrary path strings being reflected into + * queries, cache keys and flash messages. + * + * @param string|null $ip + * + * @throws \Cake\Http\Exception\BadRequestException + * + * @return string Normalized IP string. + */ + protected function assertValidIp(?string $ip): string { + $ip = (string)$ip; + if ($ip === '' || filter_var($ip, FILTER_VALIDATE_IP) === false) { + throw new BadRequestException(__d('captcha', 'Invalid IP address.')); + } + + return $ip; + } + /** * @return array */ diff --git a/src/Controller/CaptchaController.php b/src/Controller/CaptchaController.php index 6993539..75f7c91 100644 --- a/src/Controller/CaptchaController.php +++ b/src/Controller/CaptchaController.php @@ -54,6 +54,8 @@ public function beforeFilter(EventInterface $event): void { * @return \Cake\Http\Response|null|void */ public function display($id = null) { + $this->request->allowMethod('get'); + if ($id === null) { $captcha = new Captcha(); } else { diff --git a/templates/layout/captcha-admin.php b/templates/layout/captcha-admin.php index 63be591..cf2d6ec 100644 --- a/templates/layout/captcha-admin.php +++ b/templates/layout/captcha-admin.php @@ -11,8 +11,16 @@ <?= $this->fetch('title') ?: __d('captcha', 'Captcha Admin') ?> - - + +