*/ public array $allowedHostnames = []; /** * -------------------------------------------------------------------------- * Index File * -------------------------------------------------------------------------- * * Typically, this will be your `index.php` file, unless you've renamed it to * something else. If you have configured your web server to remove this file * from your site URIs, set this variable to an empty string. */ public string $indexPage = ''; /** * -------------------------------------------------------------------------- * URI PROTOCOL * -------------------------------------------------------------------------- * * This item determines which server global should be used to retrieve the * URI string. The default setting of 'REQUEST_URI' works for most servers. * If your links do not seem to work, try one of the other delicious flavors: * * 'REQUEST_URI': Uses $_SERVER['REQUEST_URI'] * 'QUERY_STRING': Uses $_SERVER['QUERY_STRING'] * 'PATH_INFO': Uses $_SERVER['PATH_INFO'] * * WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded! */ public string $uriProtocol = 'REQUEST_URI'; /* |-------------------------------------------------------------------------- | Allowed URL Characters |-------------------------------------------------------------------------- | | This lets you specify which characters are permitted within your URLs. | When someone tries to submit a URL with disallowed characters they will | get a warning message. | | As a security measure you are STRONGLY encouraged to restrict URLs to | as few characters as possible. | | By default, only these are allowed: `a-z 0-9~%.:_-` | | Set an empty string to allow all characters -- but only if you are insane. | | The configured value is actually a regular expression character group | and it will be used as: '/\A[]+\z/iu' | | DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!! | */ public string $permittedURIChars = 'a-z 0-9~%.:_\-'; /** * -------------------------------------------------------------------------- * Default Locale * -------------------------------------------------------------------------- * * The Locale roughly represents the language and location that your visitor * is viewing the site from. It affects the language strings and other * strings (like currency markers, numbers, etc), that your program * should run under for this request. */ public string $defaultLocale = 'en'; /** * -------------------------------------------------------------------------- * Negotiate Locale * -------------------------------------------------------------------------- * * If true, the current Request object will automatically determine the * language to use based on the value of the Accept-Language header. * * If false, no automatic detection will be performed. */ public bool $negotiateLocale = true; /** * -------------------------------------------------------------------------- * Supported Locales * -------------------------------------------------------------------------- * * If $negotiateLocale is true, this array lists the locales supported * by the application in descending order of priority. If no match is * found, the first locale will be used. * * IncomingRequest::setLocale() also uses this list. * * @var list */ public array $supportedLocales = [ 'ar-EG', 'ar-LB', 'az', 'bg', 'bs', 'ckb', 'cs', 'da', 'de-CH', 'de-DE', 'el', 'en', 'en-GB', 'es-ES', 'es-MX', 'fa', 'fr', 'he', 'hr-HR', 'hu', 'hy', 'id', 'it', 'km', 'lo', 'ml', 'nb', 'nl-BE', 'nl-NL', 'pl', 'pt-BR', 'ro', 'ru', 'sv', 'ta', 'th', 'tl', 'tr', 'uk', 'ur', 'vi', 'zh-Hans', 'zh-Hant', ]; /** * -------------------------------------------------------------------------- * Application Timezone * -------------------------------------------------------------------------- * * The default timezone that will be used in your application to display * dates with the date helper, and can be retrieved through app_timezone() * * @see https://www.php.net/manual/en/timezones.php for list of timezones * supported by PHP. */ public string $appTimezone = 'UTC'; /** * -------------------------------------------------------------------------- * Default Character Set * -------------------------------------------------------------------------- * * This determines which character set is used by default in various methods * that require a character set to be provided. * * @see http://php.net/htmlspecialchars for a list of supported charsets. */ public string $charset = 'UTF-8'; /** * -------------------------------------------------------------------------- * Force Global Secure Requests * -------------------------------------------------------------------------- * * If true, this will force every request made to this application to be * made via a secure connection (HTTPS). If the incoming request is not * secure, the user will be redirected to a secure version of the page * and the HTTP Strict Transport Security (HSTS) header will be set. */ public bool $forceGlobalSecureRequests = false; /** * -------------------------------------------------------------------------- * Reverse Proxy IPs * -------------------------------------------------------------------------- * * If your server is behind a reverse proxy, you must whitelist the proxy * IP addresses from which CodeIgniter should trust headers such as * X-Forwarded-For or Client-IP in order to properly identify * the visitor's IP address. * * You need to set a proxy IP address or IP address with subnets and * the HTTP header for the client IP address. * * Here are some examples: * [ * '10.0.1.200' => 'X-Forwarded-For', * '192.168.5.0/24' => 'X-Real-IP', * ] * * @var array */ public array $proxyIPs = []; /** * -------------------------------------------------------------------------- * Content Security Policy * -------------------------------------------------------------------------- * * Enables the Response's Content Secure Policy to restrict the sources that * can be used for images, scripts, CSS files, audio, video, etc. If enabled, * the Response object will populate default values for the policy from the * `ContentSecurityPolicy.php` file. Controllers can always add to those * restrictions at run time. * * For a better understanding of CSP, see these documents: * * @see http://www.html5rocks.com/en/tutorials/security/content-security-policy/ * @see http://www.w3.org/TR/CSP/ */ public bool $CSPEnabled = false; public function __construct() { parent::__construct(); // Solution for CodeIgniter 4 limitation: arrays cannot be set from .env // See: https://github.com/codeigniter4/CodeIgniter4/issues/7311 $envAllowedHostnames = getenv('app.allowedHostnames'); if ($envAllowedHostnames !== false && trim($envAllowedHostnames) !== '') { $this->allowedHostnames = array_values(array_filter( array_map('trim', explode(',', $envAllowedHostnames)), static fn (string $hostname): bool => $hostname !== '' )); } $this->https_on = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_ENV['FORCE_HTTPS']) && $_ENV['FORCE_HTTPS'] == 'true'); $host = $this->getValidHost(); $this->baseURL = $this->https_on ? 'https' : 'http'; $this->baseURL .= '://' . $host . '/'; $this->baseURL .= str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']); } /** * Validates and returns a trusted hostname. * * Security: Prevents Host Header Injection attacks (GHSA-jchf-7hr6-h4f3) * by validating the HTTP_HOST against a whitelist of allowed hostnames. * * In production: Fails fast if allowedHostnames is not configured. * In development: Allows localhost fallback with an error log. * * @return string A validated hostname * @throws \RuntimeException If allowedHostnames is not configured in production */ private function getValidHost(): string { $httpHost = $_SERVER['HTTP_HOST'] ?? 'localhost'; // Determine environment // CodeIgniter's test bootstrap sets $_SERVER['CI_ENVIRONMENT'] = 'testing' // Check $_SERVER first, then $_ENV, then fall back to 'production' $environment = $_SERVER['CI_ENVIRONMENT'] ?? $_ENV['CI_ENVIRONMENT'] ?? getenv('CI_ENVIRONMENT') ?: 'production'; if (empty($this->allowedHostnames)) { $errorMessage = 'Security: allowedHostnames is not configured. ' . 'Host header injection protection is disabled. ' . 'Set app.allowedHostnames in your .env file. ' . 'Example: app.allowedHostnames = "example.com,www.example.com" ' . 'Received Host: ' . $httpHost; // Production: Fail explicitly to prevent silent security vulnerabilities // Testing and development: Allow localhost fallback if ($environment === 'production') { throw new \RuntimeException($errorMessage); } log_message('error', $errorMessage . ' Using localhost fallback (development only).'); return 'localhost'; } if (in_array($httpHost, $this->allowedHostnames, true)) { return $httpHost; } // Host not in whitelist - use first configured hostname as fallback log_message('warning', 'Security: Rejected HTTP_HOST "' . $httpHost . '" - not in allowedHostnames whitelist. ' . 'Using fallback: ' . $this->allowedHostnames[0] ); return $this->allowedHostnames[0]; } }