security
Security Model
A security model describes the boundaries, trusted components, assets, and controls in an application. It helps developers reason about risk before reaching for isolated fixes.
What Matters
- List assets worth protecting: accounts, personal data, money, files, secrets, and administrative actions.
- Treat request data, uploaded files, third-party responses, and queue messages as untrusted input.
- Apply least privilege: each user, process, and credential should have only the access it needs.
- Fail closed when a permission check, dependency, or security control cannot complete safely.
- Keep logs useful for investigation without recording passwords, tokens, or unnecessary personal data.
Practical Example
<?php
declare(strict_types=1);
function controlForBoundary(string $boundary): string
{
return match ($boundary) {
'html_output' => 'escape for HTML context',
'database_query' => 'use prepared statements',
'state_change' => 'require authorisation and CSRF protection',
'uploaded_file' => 'validate content, size, name, and storage path',
default => 'identify trust boundary before choosing a control',
};
}
echo controlForBoundary('state_change') . PHP_EOL;
// Prints:
// require authorisation and CSRF protection
In Application Work
In review work, start with the route or command boundary and trace data through validation, authorisation, storage, output, and logging. This makes missing controls visible.
Start With A Data Flow
Security reviews become practical when you follow one piece of data through the application. For a profile-image upload, ask:
Who can submit the request?
Which file properties come from the browser?
Where is the file validated?
Where is it stored?
Can the web server execute or serve it directly?
Who can download it later?
What is written to logs?
This is more useful than adding a generic "security check" at the end of development. Each answer identifies a boundary where a specific control belongs.
Threats, Controls, And Residual Risk
A control reduces a risk; it rarely removes every risk by itself. Prepared statements protect SQL values, but they do not authorise which records a user may read. Authentication identifies a user, but it does not prove the user may issue a refund. Output escaping protects one rendering context, but it does not validate a file path.
Layer controls deliberately. Record the risks that remain, especially around privileged actions, third-party services, and operational access.
Least Privilege In PHP Projects
Least privilege applies beyond application users. The database account used by the web app should not normally create tables. A queue worker should receive only the secrets it needs. An upload directory should not be executable. Production logs should not be readable by every process on the server.
What To Check
Before moving on, make sure you can:
- identify assets, trust boundaries, and attacker-controlled values;
- choose controls for the correct boundary;
- explain least privilege and fail-closed behaviour;
- recognise why layered controls matter;
- trace one feature from request boundary to storage and output.
Practice
Practice: Map Security Controls
Create a helper that maps application boundaries to appropriate controls.
Requirements
- Cover HTML output, SQL queries, state-changing requests, and uploads.
- Return a deliberate fallback for unknown boundaries.
- Show that different boundaries receive different controls.
Show solution
The mapping keeps the reason for each control visible.
<?php
declare(strict_types=1);
function securityControl(string $boundary): string
{
return match ($boundary) {
'html' => 'escape output',
'sql' => 'bind prepared-statement parameters',
'state_change' => 'authorise and verify CSRF token',
'upload' => 'validate and store outside the public web root',
default => 'review the trust boundary',
};
}
foreach (['html', 'sql', 'state_change', 'upload'] as $boundary) {
echo $boundary . ': ' . securityControl($boundary) . PHP_EOL;
}
// Prints:
// html: escape output
// sql: bind prepared-statement parameters
// state_change: authorise and verify CSRF token
// upload: validate and store outside the public web root
The helper is intentionally small: it demonstrates that secure development is boundary-specific.