Application and operational security
Status: Complete. Last reviewed 2026-08-28.
Security is preservation of named assets against plausible actors and abuse paths. Framework defaults reduce common risk only when code stays inside their assumptions. A senior design identifies trust boundaries, makes authorization and data isolation structural, limits blast radius, and prepares detection and response for controls that fail.
Threat-model the boundary
Section titled “Threat-model the boundary”Identify assets (accounts, tenant data, money, secrets, availability), actors, entry points, trust transitions, and attacker goals. Trace data from browser/API/webhook/file/queue through parsing, authorization, storage, rendering, logging, and external calls. Include insiders, compromised credentials/dependencies, automation, and accidents—not only anonymous internet attackers.
For each threat, state prevention, detection, response, and residual risk. Prioritize by plausible impact and reach rather than a generic checklist. Record assumptions such as “only our proxy can set this header” or “object IDs are unguessable”; these are testable dependencies, not protection by themselves.
Minimize exposed capability. Services and humans get the least privileges needed, separated by environment and tenant where feasible. Deny by default, make sensitive operations explicit, and avoid shared credentials. Rate, size, time, and cardinality limits are security boundaries as well as performance controls.
Identity, authorization, and tenant isolation
Section titled “Identity, authorization, and tenant isolation”Authentication establishes an identity with a stated assurance. Authorization decides whether that identity can perform a capability on a resource in context. Tenant isolation limits which organization’s objects can be addressed. Validation checks input shape. These controls do not replace one another.
Enforce authorization server-side on every operation, including reads, exports, bulk endpoints, background jobs, and indirect references. Prefer querying through tenant/owner scope and then applying capability policy rather than fetching globally and hoping a later check runs. Object identifiers are not secrets; UUIDs reduce guessing but do not prevent broken object-level authorization.
Central policy code improves consistency, while database row-level security or separate schemas/databases can add defense in depth. Database isolation still needs correctly set tenant context, migration/admin paths, connection-pool reset, and tests. Queue payloads should carry stable IDs and resolve fresh authorized scope; do not serialize a request’s authenticated object and assume authorization remains valid later.
Sessions require unpredictable identifiers, secure cookie policy, rotation after authentication/privilege change, idle/absolute expiry, revocation, and CSRF protection for ambient credentials. Tokens need narrow audience/scope, expiry, secure storage/transport, key rotation, and revocation strategy. JWT signature validation must also verify allowed algorithm, issuer, audience, time claims, and application authorization; decoding is not trust.
Injection and contextual output
Section titled “Injection and contextual output”Injection occurs when untrusted data is interpreted as instructions in a grammar. Parameter binding protects SQL values, not dynamic table/column names, sort direction, raw fragments, or unsafe expressions. Map dynamic identifiers from an allowlist and keep query construction in reviewed boundaries.
Shell commands, paths, templates, LDAP, regular expressions, logs, and spreadsheets have different grammars. Prefer structured APIs without a shell; when process execution is necessary, pass an argument vector, validate the executable/inputs, restrict privileges and environment, and impose time/resource limits. For filesystem paths, generate storage names, canonicalize under an owned root, and prevent traversal/symlink surprises according to the operation.
Output encoding is destination-specific. HTML text, attribute, URL, JavaScript, and CSS contexts differ. Template auto-escaping helps only in its intended context. If user-authored HTML is required, sanitize with a maintained allowlist and still use Content Security Policy as defense in depth. Encoding malicious HTML does not make it safe to later decode into an executable context.
Log injection and secret leakage matter operationally. Use structured logging, escape/control line breaks in text sinks, and redact credentials, cookies, tokens, authorization fields, payment/personal payloads, and signed webhook bodies. An attacker should not be able to forge audit events through untrusted message text.
Browser and request threats
Section titled “Browser and request threats”CSRF abuses automatically attached credentials; defend state changes with framework tokens, suitable SameSite cookies, and origin checks where appropriate. CORS controls browser cross-origin reads/requests, not authentication or authorization. XSS runs in the trusted origin and can act with the user’s privileges, so prevent it through contextual encoding, avoiding unsafe DOM/sinks, sanitization for allowed HTML, and CSP/nonces as layered mitigation.
Clickjacking protections restrict framing through CSP frame-ancestors or compatible headers. Open redirects can support phishing/token leakage; allow only local or approved destinations. Sensitive responses need deliberate cache policy, and secrets/tokens should not enter URLs where histories, referrers, analytics, and logs capture them.
SSRF, uploads, and deserialization
Section titled “SSRF, uploads, and deserialization”SSRF turns a server’s network position and credentials into attacker capability. Parse URLs with one trusted implementation, allow approved schemes/hosts where possible, resolve and reject loopback/private/link-local/metadata ranges, recheck redirects and resolved destinations, and enforce egress network policy. DNS can change between validation and connection, so application validation alone is insufficient. Disable unnecessary protocols and response sizes/timeouts.
Uploads are untrusted bytes plus untrusted metadata. Enforce size/count, inspect magic/content rather than trusting client MIME or extension, generate names, store outside executable/public paths, scan or transform according to threat, and serve with safe content type/disposition. Image/document parsers are attack surfaces; isolate resource-intensive processing and prevent decompression bombs.
Never deserialize untrusted native PHP objects. Magic methods and gadget chains can execute behavior before application validation. Prefer schemas and plain data formats with allowlisted types and bounded depth/size. Signed serialized blobs become dangerous if the signing key leaks; signatures provide authenticity, not safety of executing object graphs.
Passwords, cryptography, and secrets
Section titled “Passwords, cryptography, and secrets”Passwords are hashed with a current adaptive password-hashing API and per-password salts managed by the implementation; they are not encrypted for recovery. Rehash when policy changes. Apply breached-password and rate/abuse controls without locking attackers into an easy denial-of-service primitive. Multi-factor and recovery flows need equal threat modelling.
Use established authenticated-encryption and signature libraries with explicit key purpose, version, nonce requirements, and rotation. Encryption without integrity permits tampering. Hashing, MACs, signatures, and encryption solve different problems. Do not invent algorithms or reuse one key for sessions, data encryption, webhook signing, and password peppers.
Secrets belong in a managed mechanism with least-privilege retrieval, audit, rotation, and separation by environment. Avoid source, images, CI logs, exception pages, shell history, and broad environment dumps. A secret leak response identifies every use, rotates/revokes dependents, evaluates encrypted/signed data and sessions, searches exposure, and records a timeline. Rotation must be rehearsed before compromise.
Availability, abuse, and supply chain
Section titled “Availability, abuse, and supply chain”Authentication and expensive endpoints need per-identity/IP/device/risk limits, but shared networks and attacker-controlled identifiers make simple counters imperfect. Bound request bodies, parsing depth, database result size, upload expansion, regex/computation, concurrent work, and outbound calls. Prefer graceful degradation and admission control so one tenant cannot exhaust shared pools.
Dependencies, build actions, Composer plugins/scripts, container bases, and update channels execute trusted code. Pin/review lock and image digests as appropriate, restrict plugin/script execution, scan advisories and provenance, minimize dependencies, isolate builds, and keep patch capability. An advisory-free package can still be compromised; ownership and behavior review remain necessary.
Production debug output is disabled, security headers are tested at the edge, and privileged/admin surfaces have stronger network/identity controls and audit. Backups are encrypted, access-controlled, and restore-tested; otherwise ransomware or operator deletion can defeat application controls.
Detection and incident response
Section titled “Detection and incident response”Log successful and denied sensitive actions with actor, target scope, outcome, request/operation ID, and safe source context. Protect audit log integrity and retention. Alert on meaningful behavior—credential stuffing success, privilege changes, bulk exports, key use anomalies, signature failures, tenant-scope denials—not every 4xx.
An incident plan covers containment, credential/key rotation, session/token revocation, forensic preservation, dependency/host isolation, customer/legal communication, recovery, and recurrence prevention. Assume timestamps/logs may be incomplete. Test playbooks and access before an incident; the responder should not need a compromised production credential to rotate it.
Current and legacy context
Section titled “Current and legacy context”- Current: Use the current OWASP ASVS and cheat-sheet guidance plus current PHP/framework APIs; verify algorithms, browser behavior, and dependency support at implementation time.
- Common: Cookie sessions, API tokens, OAuth/OIDC, signed webhooks, cloud metadata, CI identities, and third-party packages create overlapping trust boundaries.
- Legacy: MD5/SHA password hashes, permanent shared keys, raw output, global tenant lookups, native object serialization, and IP-only trust need staged replacement with monitoring and compatibility plans.
Interview practice
Section titled “Interview practice”- BACKEND-SECURITY-01 — Threat-model a tenant export
- BACKEND-SECURITY-02 — Separate injection controls
- BACKEND-SECURITY-03 — Defend an outbound fetcher from SSRF
- BACKEND-SECURITY-04 — Design secure uploads
- BACKEND-SECURITY-05 — Respond to an application-key leak
- BACKEND-SECURITY-06 — Secure authentication and recovery