Security
Mass Assignment Protection
Mass assignment is one of the most common security vulnerabilities in ORM systems. WPZylos Model provides two mechanisms to protect against it.
Always Define $fillable or $guarded
// Good: Good: Explicit whitelist
class User extends Model
{
protected array $fillable = ['name', 'email', 'bio'];
}
// Good: Good: Explicit blacklist
class User extends Model
{
protected array $guarded = ['id', 'is_admin', 'role'];
}
// Bad: Dangerous: No protection
class User extends Model
{
// $fillable and $guarded are both empty!
}
Guard Everything by Default
If you want maximum security:
protected array $guarded = ['*'];
Use forceFill() only in trusted contexts:
// Only in admin controllers or internal logic
$user->forceFill(['is_admin' => true]);
Hidden Attributes
Prevent sensitive data from leaking in API responses:
class User extends Model
{
protected array $hidden = [
'password',
'api_key',
'secret_token',
'remember_token',
];
}
// $user->toArray() and $user->toJson() will not include hidden attributes
echo json_encode($user); // Safe — no password in output
SQL Injection
The Model layer delegates all SQL generation to the QueryBuilder from wpzylos-database, which uses WordPress's $wpdb->prepare() for parameterized queries. This provides SQL injection protection by default.
However, be careful with:
- Never interpolate user input directly into raw SQL
- Always use the query builder methods (
where(),insert(),update()) - Validate and sanitize user input before passing to models
// Good: Safe: uses parameterized queries
$user = User::where('email', $userInput)->first();
// Good: Safe: mass assignment is filtered
$user = User::create($request);
// Bad: Dangerous: never do this
$wpdb->query("SELECT * FROM users WHERE email = '{$userInput}'");
Input Validation
Always validate data before passing to models:
// Validate before creating
$name = sanitize_text_field($_POST['name'] ?? '');
$email = sanitize_email($_POST['email'] ?? '');
if (empty($name) || !is_email($email)) {
wp_send_json_error('Invalid input');
}
$user = User::create([
'name' => $name,
'email' => $email,
]);
Mutators for Data Sanitization
Use mutators to automatically sanitize data:
class User extends Model
{
public function setEmailAttribute($value): void
{
$this->attributes['email'] = sanitize_email(strtolower(trim($value)));
}
public function setNameAttribute($value): void
{
$this->attributes['name'] = sanitize_text_field($value);
}
}
Soft Deletes and Data Retention
When using soft deletes, remember:
- Soft-deleted records still exist in the database
- Ensure compliance with data retention policies (GDPR, etc.)
- Use
forceDelete()when permanent removal is required - Consider scheduled cleanup of old soft-deleted records
Event-Based Security
Use model events for access control:
class Post extends Model
{
protected static function boot(): void
{
static::deleting(function (Post $post) {
$currentUserId = get_current_user_id();
if ($post->author_id !== $currentUserId && !current_user_can('delete_others_posts')) {
return false; // Prevent unauthorized deletion
}
});
}
}
Reporting Vulnerabilities
If you discover a security vulnerability, please report it responsibly:
Email: [email protected]
See security.md for full details.