CodeIgniter 4.7.5 security release: what to patch and how
CodeIgniter 4.7.5 fixes six advisories, including a critical upload bypass and Parser code execution. See who is exposed and how to upgrade safely.
8 min read

In this article
CodeIgniter 4.7.5, released on 9 October 2026, is a security release. It patches six published advisories: one rated critical (an upload validation bypass), two high (code execution through View Parser templates and view data keys) and three medium (a multi-file upload bypass, a Parser cross-site scripting bug and a CURLRequest credential leak). Every advisory lists all versions below 4.7.5 as affected, so if you run any CodeIgniter 4 application, plan the upgrade now.
The release also contains two small breaking changes and around thirty bug fixes. This guide explains each advisory in plain terms, shows who is actually exposed, and walks through the upgrade and the checks to run afterwards. Everything here comes from the official 4.7.5 change log, the upgrade guide and the GitHub security advisories published the same day.
The six advisories at a glance
| Area | Severity | You are exposed if | Action besides upgrading |
|---|---|---|---|
Upload rules is_image, mime_in, ext_in | Critical | You keep client filenames in a web-accessible folder that executes PHP | Store uploads outside public/ with generated names |
| Multi-file upload validation | Medium | An optional multiple-file field is validated without uploaded[field] | Re-test multi-file forms |
| View Parser substitution | Medium (XSS) | User data is rendered through the Parser | Check templates that relied on re-parsing |
| Parser conditionals | High | Less-trusted users can edit Parser templates | Enable restrictParserConditionals; upgrading alone is not enough |
| View data keys | High | Untrusted input controls the keys template, view or foundView | Never pass user-controlled array keys as view data |
CURLRequest after an exception | Medium | You reuse one client for different destinations after a failed request | Use separate clients per trust level |

1. Upload validation bypass (critical)
Before 4.7.5, the is_image and mime_in rules could accept a client filename with trailing dots that hid a PHP extension, and those rules plus ext_in could accept a PHP extension placed before the final one, such as shell.php.gif. A GIF/PHP polyglot with such a name passed validation. If the application then saved the file under its original name in a public folder where the web server runs PHP, an attacker could execute code.
The fix rejects filenames ending in dots and any PHP handler extension before the final extension, including names revealed by filename sanitisation (shell.p$hp.gif becomes shell.php.gif). The check runs on the client filename, so it also rejects an innocent logo.php.gif; users must rename such files, for example to logo-php.gif.
The safest pattern was already the documented one: validate, then let the framework choose the stored name and keep the file out of the web root.
public function upload()
{
$rules = [
'avatar' => [
'label' => 'Avatar',
'rules' => 'uploaded[avatar]|is_image[avatar]|mime_in[avatar,image/jpeg,image/png,image/webp]|max_size[avatar,2048]',
],
];
if (! $this->validateData([], $rules)) {
return redirect()->back()->withInput();
}
$file = $this->request->getFile('avatar');
// Saved under writable/uploads/avatars with a random name.
$path = $file->store('avatars/');
return redirect()->to('/profile')->with('message', 'Avatar uploaded');
}If uploads must be public, the advisory says to disable script execution in that directory and use $file->move($path, $file->getRandomName()).
2. Multi-file uploads with an empty entry (medium)
The max_size, is_image, mime_in, ext_in, max_dims and min_dims rules returned success as soon as they met an entry with UPLOAD_ERR_NO_FILE. By putting an empty file before a disallowed one in a multiple-file field, an attacker could skip type, size and dimension checks for the rest. Both the strict and the traditional rule classes were affected. From 4.7.5, every non-empty file is checked. If you accept several files in one field, add a test that submits an empty entry followed by a real file and confirms the bad file is rejected.
3. View Parser: cross-site scripting and template code execution
Three of the advisories concern views, and they matter most to applications that use the View Parser ({variable} templates) rather than plain PHP views.
Substituted values are now data only
Previously a value containing Parser syntax, such as {text|raw} or {!text!}, could be interpreted by a later substitution pass. An attacker could use that to print another value without escaping, which is stored XSS. Now substituted values, and the output of built-in plugins, are never parsed again. This is the first breaking change: code that relied on nested placeholders inside values will now print them literally. Render the fragment first and insert it with {! !}:
$parser = service('parser');
$greeting = $parser->setData(['name' => 'Bob'])->renderString('Hello, {name}!');
echo $parser->setData(['greeting' => $greeting])
->renderString('<p>{! greeting !}</p>');Restricted conditionals are opt-in
Conditions in {if} and {elseif} tags are evaluated as PHP. If your application lets customers or staff edit Parser templates, such as email templates, anyone with that access can run PHP on your server. Version 4.7.5 adds a restricted mode that allows only variables, literals, arithmetic, comparison and logical operators, and parentheses. It is off by default, so the advisory is explicit: upgrading alone does not remove the exposure.
// Per render, for a template written by a less-trusted user:
$html = $parser->setData(['first_name' => $user->first_name])
->renderString($emailTemplate, ['restrictConditionals' => true]);
// Or as the default, in app/Config/View.php:
// public bool $restrictParserConditionals = true;Restriction only limits conditionals. Pseudo-variables can still read any view data you pass, so pass only what the template author is allowed to see.
View data keys could replace the template
View data was extracted into the same scope as the renderer's own variables. A data key named template in Parser::render() or renderString(), view in View::renderString(), or foundView in view cells could replace the template source or the included file path. Exploitation needs untrusted input to control those keys, which usually means code that passes request arrays straight into a view. Do not do that, even after upgrading.

4. CURLRequest could resend credentials (medium)
When CURLRequest::request() threw an exception, request-specific options could stay on the client even with shareOptions set to false. With the default http_errors setting, any 4xx or 5xx response takes that path. If the application caught the exception and reused the client for another host, the next request could carry the previous Authorization header, Basic Auth credentials or body.
From 4.7.5, request options are cleared after a failure, and per-request baseURI and delay values are reset after every request. That is the behaviour change to check: if your code retries a failed request, pass its options again and reapply anything set with setAuth(), setBody(), setForm() or setJSON().
use CodeIgniter\HTTP\Exceptions\HTTPException;
$options = ['headers' => ['Authorization' => 'Bearer ' . $token], 'timeout' => 10];
$client = service('curlrequest', ['baseURI' => 'https://api.example.com/']);
try {
$response = $client->request('GET', 'v1/orders', $options);
} catch (HTTPException $e) {
// Retry with the options passed again; they are not kept after a failure.
$response = $client->request('GET', 'v1/orders', $options);
}A better habit is one client per destination, so a token meant for one API can never be sent to another.
How to upgrade from 4.7.4
- Update the framework. For an app-starter project, run
composer update codeigniter4/frameworkand confirm the version withcomposer show codeigniter4/framework. Manual installations replace thesystem/folder. - Merge project files. Composer never touches
app/. The upgrade guide lists two changed files:app/Config/View.php(new$restrictParserConditionalsproperty, defaultfalse) andapp/Controllers/BaseController.php. Compare them with the versions invendor/codeigniter4/framework/app/. - Search for affected patterns. Grep for
renderString(,->render(on the parser,CURLRequestorcurlrequest, andgetName()orgetClientName()used as a stored filename. - Run your tests, especially upload forms, Parser templates and code that retries HTTP calls.
- Deploy with your normal process and clear any config or file-locator cache.
If you are still on 4.6 or older, read the 4.7 upgrade notes first: 4.7 requires PHP 8.2 or newer.
Other fixes worth knowing about
File::move()andUploadedFile::move()now create files with0666 & ~umask()instead of executable0777permissions.- Honeypot bot detection returns 403 instead of 500.
- PostgreSQL query failures now throw a
DatabaseExceptionwhenDBDebugis on, even with PHP warnings disabled. Time::today(),yesterday()andtomorrow()respect the timezone you pass andsetTestNow().valid_cc_numberrejects non-digit characters.- Empty Content-Security-Policy headers are no longer sent.
make:migration --sessionreports an error for drivers other than MySQLi and Postgre instead of generating a broken migration.
Gotchas and what to do next
- Parser users must act twice: upgrade, then enable restricted conditionals for any template a less-trusted person can edit.
- Expect a few rejected uploads with names like
report.php.pdfor names ending in a dot. Tell support staff the fix is to rename the file. - Retries change: code that caught an HTTP exception and called
request()again without options will now send a bare request. - Shield and other packages are separate Composer packages; run
composer outdatedandcomposer auditwhile you are there.
Subscribe to the security advisories on the CodeIgniter4 GitHub repository so the next release reaches you on the day it is published, and keep this upgrade as a small, separate deploy so it is easy to roll back if a template or upload form needs attention.
Frequently asked questions
Which CodeIgniter versions are affected by the October 2026 advisories?
All six GitHub advisories list versions below 4.7.5 as vulnerable and 4.7.5 as the patched version. Older 4.x lines receive no separate patches, so the fix is to upgrade to 4.7.5 or later.
Is upgrading to 4.7.5 enough to fix the Parser conditionals issue?
No. Restricted conditionals are opt-in. If less-trusted users can edit View Parser templates, set
Config\View::$restrictParserConditionalstotrueor passrestrictConditionalsfor those renders.Why does 4.7.5 reject a harmless file such as logo.php.gif?
The new checks reject any PHP handler extension before the final extension, regardless of the file contents, because that pattern was used to smuggle polyglot files. Rename the file, for example to
logo-php.gif.Am I affected if I only use plain PHP views?
The two Parser advisories do not apply, but the view data key issue also covers
View::renderString()with a key namedview, and view cells withfoundView. The upload and CURLRequest fixes apply to any application using those features.Which project files should I merge after updating?
The upgrade guide lists
app/Config/View.php, which gains$restrictParserConditionals, andapp/Controllers/BaseController.php. Composer does not change files inapp/, so compare them manually.What changed for CURLRequest retries?
After a failed request, request-specific options are cleared even when
shareOptionsisfalse. Pass the options again on retry and reapplysetAuth(),setBody(),setForm()orsetJSON()settings. Options given to the constructor are kept.
Your next customer could start right here.
Introduce your tool, product or business to readers exploring technology.
Explore placements and request a private quote
Sources
- Version 4.7.5 change log — CodeIgniter Foundation, 2026-10-09
- Upgrading from 4.7.4 to 4.7.5 — CodeIgniter Foundation, 2026-10-09
- CodeIgniter v4.7.5 release — GitHub, 2026-10-09
- Upload validation bypass via trailing dots and embedded PHP extensions (GHSA-4hwf-v4mp-hf6c) — GitHub, 2026-10-09
- PHP code execution through Parser conditionals in user-editable templates (GHSA-4q58-jw8x-8cm7) — GitHub, 2026-10-09
- CURLRequest may disclose credentials to a subsequent destination after an exception (GHSA-9g9v-xgwj-697h) — GitHub, 2026-10-09
- View Parser: Restricting Conditionals — CodeIgniter Foundation
Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.
Comments
No comments yet. Be the first to join the conversation.
Leave a comment
Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.
More from Cloudsoft Zone

Troubleshooting guideCodeIgniter
CodeIgniter 4 validation not working? 9 causes and fixes
Fix CodeIgniter 4 validation problems fast: wrong input source, lost errors after redirect, permit_empty, strict JSON rules, is_unique placeholders and more.
By Cloudsoft Zone

Industry updateCakePHP
What's new in CakePHP 5.4: ORM changes, DTO mapping and a built-in container
CakePHP 5.4 changes default eager loading, enforces entity-table ownership and adds request-to-DTO mapping, an optional built-in DI container, typed unhydrated queries and JSON streaming. Here is what to check before upgrading.
By Cloudsoft Zone

Laravel
Upgrading from Laravel 12 to Laravel 13: a practical guide
Laravel 12 lost bug-fix support in August 2026. This guide covers the PHP 8.3 requirement, Composer changes, the CSRF middleware rename, cache and session hardening, low-impact changes that often bite, and a safe rollout plan.
By Cloudsoft Zone
