What you'll learn
Threat-model the application instead of applying isolated security snippets. You will defend input, output, identity, authorization, outbound requests, sessions, files, dependencies, and browser behavior in layers.
By the end of this lesson, you'll be able to:
- Create a practical threat model
- Defend XSS, CSRF, injection, SSRF, and authorization boundaries
- Apply secure session and response policies
Core mental model
Professional PHP is less about memorizing APIs and more about choosing a clear boundary for each responsibility. Use this table as a decision guide while reading the examples.
| Concept | What it protects | Decision rule |
|---|---|---|
| Trust boundary | Explicit untrusted transition | Validate whenever data crosses into a more trusted component. |
| Authorization | Permitted action | Check server-side for every resource and operation. |
| Defense in depth | Failure containment | Combine independent controls; do not rely on one filter. |
Professional workflow
Build the feature in small, verifiable steps. Each step leaves the system in a state you can test.
- Describe the security trust boundary boundary: its inputs, outputs, invariants, and expected failures.
- Implement the smallest happy path behind an explicit contract.
- Add validation and translate low-level failures into language the caller understands.
- Exercise the boundary with realistic data, then inspect output, logs, and resource cleanup.
- Refactor only after behavior is protected by a repeatable check.
Make the boundary visible
Guided code lab
Issue and verify CSRF tokens
The token is unpredictable, session-bound, and compared without timing leaks.
<?php
function csrfToken(): string
{
return $_SESSION['csrf'] ??= bin2hex(random_bytes(32));
}
function verifyCsrf(string $provided): void
{
$expected = $_SESSION['csrf'] ?? '';
if ($expected === '' || !hash_equals($expected, $provided)) {
throw new RuntimeException('Invalid CSRF token');
}
}Constrain outbound URLs
A trusted base URL is configured; user input chooses only an encoded path segment, preventing arbitrary internal requests.
<?php
final readonly class AvatarClient
{
public function __construct(private string $trustedBaseUrl) {}
public function fetch(string $username): string
{
if (!preg_match('/^[a-z0-9_-]{1,32}$/i', $username)) {
throw new InvalidArgumentException('Invalid username');
}
$url = rtrim($this->trustedBaseUrl, '/') . '/' . rawurlencode($username);
return $this->httpGet($url, timeoutSeconds: 2, maxBytes: 1_000_000);
}
}Production practice
Contract
Define assets, actors, entry points, abuse cases, and controls; security requirements become testable behavior.
Verification
Add negative tests for cross-user access, missing tokens, malicious markup, unsafe URLs, and oversized payloads.
Operations
Patch dependencies, rotate secrets, alert on abuse patterns, and maintain a rehearsed incident response path.
Common failure mode
Independent workshop
Threat-model and harden a profile application with uploads, password changes, admin search, and avatar fetching.
Your finished workshop must include:
- Prioritized threat model
- Layered controls and security headers
- Automated abuse-case suite and incident note
Definition of done
Recap & quick check
Key takeaways
- Security starts with assets and trust boundaries.
- Validation and encoding are context-specific.
- Authentication is identity; authorization is permission.
- Independent controls limit mistakes.
Quick check
1. What stops script execution in rendered user text?
2. What does CSRF exploit?
3. Where should object authorization run?
Keep the workshop: later phases deliberately build on these boundaries, so today's small example can become part of your portfolio architecture.