Skip to content

Conversation

@dereuromark
Copy link
Member

@dereuromark dereuromark commented Nov 24, 2025

Summary

This PR fixes a critical security vulnerability that allows Host Header Injection attacks, which can be exploited to hijack password reset tokens and compromise user accounts.

The Vulnerability

The current implementation in config/bootstrap.php (lines 168-170) dynamically sets App.fullBaseUrl from the HTTP_HOST header when not configured:

$httpHost = env('HTTP_HOST');
if ($httpHost) {
    $fullBaseUrl = 'http' . $s . '://' . $httpHost;  // ⚠️ Trusts attacker input!
}

Attack Scenario

  1. Attacker sends password reset request with malicious Host header: Host: attacker.com
  2. Application generates reset URL: http://attacker.com/users/reset_password/valid-token-123
  3. Victim receives email and clicks the malicious link
  4. Attacker captures the valid reset token from their server logs
  5. Attacker uses stolen token on legitimate domain to reset victim's password → Account takeover

The Fix

1. Changed config/app.php:

  • Set App.fullBaseUrl to use APP_FULL_BASE_URL environment variable (instead of false)
  • Enhanced security documentation explaining the risk
  • Added clear configuration instructions

2. Enhanced config/bootstrap.php:

  • Production mode: Throws exception if App.fullBaseUrl is not configured
  • Development mode: Keeps HTTP_HOST fallback for convenience (with explicit warning)
  • Added comprehensive security comments

3. Updated config/.env.example:

  • Added APP_FULL_BASE_URL configuration with security documentation
  • Provides example value for developers

Impact

Development

No breaking changes - HTTP_HOST fallback still works in debug mode

Production

⚠️ Applications MUST configure APP_FULL_BASE_URL or will fail with clear error:

SECURITY: App.fullBaseUrl is not configured. 
This is required in production to prevent Host Header Injection attacks.
Set APP_FULL_BASE_URL environment variable or configure App.fullBaseUrl in config/app.php

This is intentional to force proper security configuration in production environments.

As this only applies to new apps, this is BC. current apps are most likely vulnerable in many cases, though.
Maybe debug kit or sth could warn here.

Configuration Options

Developers can configure this in multiple ways:

Option 1: Environment Variable (Recommended)

APP_FULL_BASE_URL=https://yourdomain.com

Option 2: config/app(_local).php

'App' => [
    'fullBaseUrl' => 'https://yourdomain.com',
]

Testing

Manual testing performed:

  • ✅ Development mode with debug=true: Works as before (uses HTTP_HOST)
  • ✅ Production mode without config: Throws clear security exception
  • ✅ Production mode with config: Works correctly with configured URL

References

Related Security Concern

The existing comment in config/app.php (line 42) already mentions "if you are concerned about people manipulating the Host header" but didn't enforce it. This PR makes that concern actionable by requiring explicit configuration in production.

dereuromark and others added 2 commits November 24, 2025 18:40
This commit fixes a critical security vulnerability that allows
Host Header Injection attacks, which can be used to hijack password
reset tokens and other security-critical operations.

Changes:
1. Updated config/app.php:
   - Changed App.fullBaseUrl default from 'false' to env('APP_FULL_BASE_URL')
   - Enhanced documentation to explicitly warn about security implications
   - Added clear instructions for proper configuration

2. Updated config/bootstrap.php:
   - Added security validation that throws exception in production if
     App.fullBaseUrl is not configured
   - Retained HTTP_HOST fallback ONLY for development mode
   - Added explicit security warnings in comments

3. Updated config/.env.example:
   - Added APP_FULL_BASE_URL with security documentation
   - Provides example value for developers to configure

Impact:
- Development: No breaking changes (HTTP_HOST still used as fallback)
- Production: Applications MUST set APP_FULL_BASE_URL or will fail
  with clear error message explaining the security requirement

Attack Vector:
Without this fix, attackers can send malicious Host headers in
password reset requests, causing the application to generate
reset links pointing to attacker-controlled domains. When victims
click these links, attackers capture valid reset tokens and can
compromise accounts.

References:
- OWASP Host Header Injection
- https://portswigger.net/web-security/host-header

🤖 Generated with Claude Code
The security check was throwing an exception during PHPStan static
analysis because it runs with debug=false but no HTTP_HOST. Modified
the logic to only enforce the fullBaseUrl requirement when in a web
request context (HTTP_HOST is present). This allows CLI tools like
PHPStan to load the bootstrap without errors while still maintaining
the security check for actual web requests in production.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
config/app.php Outdated
'wwwRoot' => WWW_ROOT,
//'baseUrl' => env('SCRIPT_NAME'),
'fullBaseUrl' => false,
'fullBaseUrl' => env('APP_FULL_BASE_URL'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't removing the false break local dev setups?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we keep false here then as default?

@markstory markstory merged commit 89d6e6d into cakephp:5.x Nov 25, 2025
3 checks passed
@LordSimal
Copy link
Contributor

LordSimal commented Jan 16, 2026

This is a bad idea as this generates a blank page without any error for debug => false in the default setup as its before ErrorHandler middleware has been set up + no log message in logs/error.log as Log::setConfig() is called after this exception.

@LordSimal
Copy link
Contributor

support chat also asked how this security issue works with a multi domain setup

sure one can dynamically create a variable before the return array in e.g. config/app_local.php and use that as fullBaseUrl but it would be better if we can come up with a separate array based config key where all allowed HTTP_HOST values are configured and only if the current domain doesn't match that we handle that.

@ADmad
Copy link
Member

ADmad commented Jan 16, 2026

..as its before ErrorHandler middleware has been set up..

Exceptions generated before the middleware queue is executed are expected to be handled by the exception trap registered here
https://github.com/cakephp/app/blob/5.x/config/bootstrap.php#L129

@LordSimal
Copy link
Contributor

LordSimal commented Jan 16, 2026

sure, they are handled there, but the only thing thats being done there is trying to log the exception which it doesn't know where, as the Log config has not yet been set
https://github.com/cakephp/app/blob/5.x/config/bootstrap.php#L203

@LordSimal
Copy link
Contributor

The WebException renderer also doesn't work as the cache config required for our I18n methods also doesn't exist yet
https://github.com/cakephp/cakephp/blob/5.x/src/Error/Renderer/WebExceptionRenderer.php#L355
https://github.com/cakephp/app/blob/5.x/config/bootstrap.php#L199

@LordSimal
Copy link
Contributor

I'd personally move that check logic into a middleware instead of doing that inside bootstrap.

with that it only applies to HTTP requests and doesn't requirey any custom fiddling around for CLI tools or testing + it happens after everything has been setup and therefore our error handling logic works as well

@ADmad
Copy link
Member

ADmad commented Jan 16, 2026

In production mode could we perhaps check for env('SSL_TLS_SNI') if 'App.fullBaseUrl' is not set?

dereuromark added a commit to dereuromark/app that referenced this pull request Jan 17, 2026
This moves the security check from bootstrap.php to a dedicated
HostHeaderMiddleware. This fixes the issue where throwing an exception
in bootstrap resulted in blank pages because error handling wasn't
initialized yet.

Benefits:
- Errors are properly rendered by ErrorHandlerMiddleware
- Logging works correctly since Log::setConfig() has already run
- CLI commands are not affected
- Validates Host header matches configured fullBaseUrl

Addresses concerns raised in cakephp#1071.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants