Course outline

PHP / Advanced: reliable models and runtime evidence / Lesson 15

Security boundaries and sensitive operations

Connect validation, SQL binding, output handling, session policy, and authorization without calling one defense a universal sanitizer. Use only synthetic local examples and safe password verification.

About 4 min readContent reviewed
On this page

Lesson goals

  • Trace untrusted data into context-specific sinks.
  • Use password APIs without exposing credentials.
  • Keep authorization and resource policies server-owned.

Separate five different security questions

Validation asks whether data fits the domain. Prepared SQL separates values from query structure. HTML escaping controls interpretation in a particular output context. Authentication establishes identity context, and authorization decides whether that actor may perform this operation on this object now. None replaces the others. A valid integer can name another learnerโ€™s record, and a safely escaped title can still disclose private information. Trace sources, transformations, and sensitive sinks rather than attaching a vague sanitized label to a value. Keep tests scoped to owned synthetic records and do not turn learning examples into scans of live systems.

Use password hashing APIs, not reversible storage

password_hash creates a stored verifier and password_verify checks a supplied password against it. Do not create a custom fast hash, store plaintext, or compare a newly generated salted hash string with the stored hash. PASSWORD_DEFAULT can change over time, so storage needs room for the returned representation and deployment policy should review supported algorithms and costs. The example uses a clearly synthetic teaching password and prints only Boolean verification outcomes, never the generated hash. Its exact hash is intentionally nondeterministic. This is API behavior evidence, not a complete authentication design including rate limits, recovery, and session transitions.

php Synthetic verifier behavior without revealing the hash
<?php
declare(strict_types=1);
$hash = password_hash("synthetic-demo-passphrase", PASSWORD_DEFAULT);
echo password_verify("synthetic-demo-passphrase", $hash) ? "match
" : "no match
";
echo password_verify("different-demo-value", $hash) ? "match
" : "no match
";
Expected output
match
no match

The stored verifier is random-salted, but verification outcomes are deterministic for these two inputs.

Authorize actor, action, and object together

A server must check access even when the interface hides a button. Validate object ownership and role scope at every supported entry point, including exports and APIs. A session role field can become stale, so sensitive rules need a current trusted source according to the application design. The pure policy example permits a learner to read only their own record while allowing a bounded administrator read. It intentionally models a small rule rather than real authentication. Production authorization needs additional organizational scope, record visibility, and operation-specific conditions that must not be inferred from this teaching shortcut.

php An explicit synthetic access policy
<?php
declare(strict_types=1);
function canRead(string $role, int $actorId, int $ownerId): bool {
    return $role === "admin" || ($role === "learner" && $actorId === $ownerId);
}
echo canRead("learner", 1, 1) ? "allow
" : "deny
";
echo canRead("learner", 1, 2) ? "allow
" : "deny
";
echo canRead("guest", 1, 1) ? "allow
" : "deny
";
Expected output
allow
deny
deny

Valid identifiers do not imply ownership permission. Unknown roles do not inherit the learner rule.

Constrain paths, uploads, redirects, and deserialization

Untrusted paths and URLs need an explicit allow policy before reaching filesystem, stream, redirect, or network APIs. Do not rely on a string prefix alone to prove a resolved path remains in an allowed directory. Uploaded content requires naming, size, content, storage, and execution policies. Avoid unserialize on untrusted data; parsing JSON into a validated domain record is a different and usually narrower boundary. Do not concatenate user input into shell commands or evaluate it as PHP. These choices protect different interpreters and resource surfaces, so a prepared database statement cannot make them safe automatically.

Keep secrets private and security claims bounded

Log compact safe context and a correlation identifier where available, not passwords, hashes, session tokens, or full private payloads. Configure public error handling separately from private diagnostics. Review dependency trust, supported runtime versions, and session settings as maintained obligations rather than one-time checklist items. A meaningful security test asserts the prohibited data or side effect is absent and includes an allowed control case. Report tested actors, objects, environments, and remaining assumptions. Passing a few local examples or a scanner does not prove an application contains no vulnerabilities, and a course completion is not a security certification.

Common errors and fixes

A single sanitized flag is trusted everywhere

Why it happens
Different interpreters and permission questions are conflated.
How to fix it
Apply context-specific policies at each trust boundary.
Prevent it next time
Review data flow into SQL, HTML, paths, and sensitive actions.

A salted password hash is recreated and compared as text

Why it happens
Verifier behavior is misunderstood.
How to fix it
Use password_verify with the stored verifier.
Prevent it next time
Test match, mismatch, and rehash policy separately.

A hidden control is treated as authorization

Why it happens
An editable client becomes the security authority.
How to fix it
Enforce actor/action/object rules on the server.
Prevent it next time
Test direct synthetic requests and no-side-effect denial.

Check your understanding

1. Why not compare password_hash(input) with a stored hash?

Show worked solution

A new salted verifier can differ even for the same password. password_verify understands the stored representation and checks the candidate correctly. Keep hashes private, support an appropriate rehash policy, and test authentication workflow protections separately from the hash API.

2. What should an unauthorized record-read test inspect beyond status?

Show worked solution

Verify that no private fields or identifiers leak and that no state-changing side effect occurs. Include an owner-allowed case to show the feature remains usable. Use synthetic actors and records in an authorized isolated environment, and state the scope rather than claiming universal access-control proof.

Key takeaways

  • Different trust boundaries require different defenses.
  • Security evidence includes prohibited effects, allowed controls, and limits.

Official references