security: validate JSON metadata fields to prevent mass assignment #14

Closed
opened 2026-02-20 02:39:55 +00:00 by Clotho · 0 comments
Member

Issue

Models with JSON metadata fields in $fillable arrays lack validation, risking large payloads or malicious data.

Affected Models

Service Model (src/Mod/Hub/Models/Service.php)

Current:

protected $fillable = [
    'name',
    'code',
    'description',
    'status',
    'metadata',  // <- No validation
    'website_class',
];

Risk:

  • metadata is JSON column, can contain arbitrary data
  • No size limit enforcement
  • No structure validation
  • Could be used to store large payloads

HoneypotHit Model (src/Mod/Hub/Models/HoneypotHit.php)

Current:

protected $fillable = [
    'path',
    'ip',
    'user_agent',
    'headers',  // <- Array, could be large
    'referer',
    'severity',
    'blocked',
];

Risk:

  • headers array can be arbitrarily large
  • Malicious bot could send huge header payloads
  • Database bloat
  • Memory exhaustion

1. Add Validation to Service Model

class Service extends Model
{
    public function setMetadataAttribute($value): void
    {
        if (is_array($value)) {
            // Validate size
            $json = json_encode($value);
            if (strlen($json) > 65535) { // 64KB limit
                throw new InvalidArgumentException('Metadata exceeds maximum size');
            }
            $this->attributes['metadata'] = $json;
        } else {
            $this->attributes['metadata'] = $value;
        }
    }
}

2. Add Validation to HoneypotHit Model

class HoneypotHit extends Model
{
    public function setHeadersAttribute($value): void
    {
        if (is_array($value)) {
            // Limit to 50 headers max
            $value = array_slice($value, 0, 50);
            
            // Validate total size
            $json = json_encode($value);
            if (strlen($json) > 16384) { // 16KB limit
                // Truncate to fit
                $value = array_slice($value, 0, 20);
            }
        }
        $this->attributes['headers'] = json_encode($value);
    }
}

3. Add Input Validation Rules

In controllers/components that create these models:

// Service creation
$validated = $request->validate([
    'metadata' => ['nullable', 'array', 'max:100'], // Max 100 keys
    'metadata.*' => ['string', 'max:1000'], // Each value max 1KB
]);

// HoneypotHit creation  
// Already handled in TeapotController via sanitizeHeaders(),
// but add size check:
if (count($headers) > 50) {
    $headers = array_slice($headers, 0, 50);
}

Additional Concerns

Service::setMeta() Method (line 143-148)

Current:

public function setMeta(string $key, mixed $value): void
{
    $meta = $this->metadata ?? [];
    $meta[$key] = $value;
    $this->metadata = $meta;
}

Issue:

  • No validation that $key is not empty
  • No validation that $key doesn't contain special characters
  • No check for reserved keys

Recommended:

public function setMeta(string $key, mixed $value): void
{
    if (empty($key) || !preg_match('/^[a-z0-9_]+$/i', $key)) {
        throw new InvalidArgumentException('Invalid metadata key');
    }
    
    $meta = $this->metadata ?? [];
    $meta[$key] = $value;
    $this->metadata = $meta;
    $this->save();
}

Testing Requirements

  • Test metadata size limits
  • Test header count limits
  • Test invalid metadata keys
  • Test JSON encoding edge cases

Priority

Medium - Defense-in-depth, not immediately exploitable but best practice.

Discovered by

Automatic codebase scan (issue #3)

## Issue Models with JSON `metadata` fields in `$fillable` arrays lack validation, risking large payloads or malicious data. ## Affected Models ### Service Model (src/Mod/Hub/Models/Service.php) **Current:** ```php protected $fillable = [ 'name', 'code', 'description', 'status', 'metadata', // <- No validation 'website_class', ]; ``` **Risk:** - `metadata` is JSON column, can contain arbitrary data - No size limit enforcement - No structure validation - Could be used to store large payloads ### HoneypotHit Model (src/Mod/Hub/Models/HoneypotHit.php) **Current:** ```php protected $fillable = [ 'path', 'ip', 'user_agent', 'headers', // <- Array, could be large 'referer', 'severity', 'blocked', ]; ``` **Risk:** - `headers` array can be arbitrarily large - Malicious bot could send huge header payloads - Database bloat - Memory exhaustion ## Recommended Fixes ### 1. Add Validation to Service Model ```php class Service extends Model { public function setMetadataAttribute($value): void { if (is_array($value)) { // Validate size $json = json_encode($value); if (strlen($json) > 65535) { // 64KB limit throw new InvalidArgumentException('Metadata exceeds maximum size'); } $this->attributes['metadata'] = $json; } else { $this->attributes['metadata'] = $value; } } } ``` ### 2. Add Validation to HoneypotHit Model ```php class HoneypotHit extends Model { public function setHeadersAttribute($value): void { if (is_array($value)) { // Limit to 50 headers max $value = array_slice($value, 0, 50); // Validate total size $json = json_encode($value); if (strlen($json) > 16384) { // 16KB limit // Truncate to fit $value = array_slice($value, 0, 20); } } $this->attributes['headers'] = json_encode($value); } } ``` ### 3. Add Input Validation Rules In controllers/components that create these models: ```php // Service creation $validated = $request->validate([ 'metadata' => ['nullable', 'array', 'max:100'], // Max 100 keys 'metadata.*' => ['string', 'max:1000'], // Each value max 1KB ]); // HoneypotHit creation // Already handled in TeapotController via sanitizeHeaders(), // but add size check: if (count($headers) > 50) { $headers = array_slice($headers, 0, 50); } ``` ## Additional Concerns ### Service::setMeta() Method (line 143-148) **Current:** ```php public function setMeta(string $key, mixed $value): void { $meta = $this->metadata ?? []; $meta[$key] = $value; $this->metadata = $meta; } ``` **Issue:** - No validation that `$key` is not empty - No validation that `$key` doesn't contain special characters - No check for reserved keys **Recommended:** ```php public function setMeta(string $key, mixed $value): void { if (empty($key) || !preg_match('/^[a-z0-9_]+$/i', $key)) { throw new InvalidArgumentException('Invalid metadata key'); } $meta = $this->metadata ?? []; $meta[$key] = $value; $this->metadata = $meta; $this->save(); } ``` ## Testing Requirements - Test metadata size limits - Test header count limits - Test invalid metadata keys - Test JSON encoding edge cases ## Priority **Medium** - Defense-in-depth, not immediately exploitable but best practice. ## Discovered by Automatic codebase scan (issue #3)
Clotho added the
review
discovery
labels 2026-02-20 02:39:55 +00:00
Charon added the
clotho
label 2026-02-20 10:57:34 +00:00
Charon added
PHP
security
P1
and removed
clotho
review
discovery
labels 2026-02-20 12:16:54 +00:00
Clotho was assigned by Charon 2026-02-20 12:20:48 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: core/php-admin#14
No description provided.