PHP application security begins at trust and privilege boundaries: validate untrusted input, encode for the exact output context, parameterize SQL values, protect identity and sessions, constrain files and networks, and keep secrets out of code and logs.
No single sanitizer secures an application. Defense comes from least privilege, current runtimes, explicit authorization, bounded resources, secure deployment headers, redacted observability, and adversarial tests for each feature.
| Operation | Question |
|---|---|
| Validation | Is this value acceptable for the domain? |
| Normalization | What canonical form should valid data use? |
| SQL parameter binding | How is data kept separate from SQL syntax? |
| HTML escaping | How is text kept separate from HTML markup? |
<?php
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
if ($email === false || $email === null) {
exit('Invalid email address.');
}
echo htmlspecialchars($email, ENT_QUOTES, 'UTF-8');
Store password hashes, never plaintext passwords. Let PASSWORD_DEFAULT choose the current recommended algorithm and allow the database column enough space for hashes to grow.
<?php
$hash = password_hash('correct horse battery staple', PASSWORD_DEFAULT);
$isValid = password_verify('correct horse battery staple', $hash);
echo $isValid ? 'Authenticated' : 'Denied';
Authenticated
A state-changing browser form should include an unpredictable token stored in the user session. Verify the submitted token with hash_equals() before applying the change. SameSite cookies help but do not replace an application CSRF design.
<?php
session_start();
$_SESSION['csrf'] ??= bin2hex(random_bytes(32));
$submitted = (string) ($_POST['csrf'] ?? '');
if (!hash_equals($_SESSION['csrf'], $submitted)) {
http_response_code(403);
exit('Invalid request token.');
}
Treat request fields, headers, cookies, uploaded files, database text, cached values, queue messages, environment configuration, and third-party responses as untrusted until validated for their use. Trust depends on provenance and current authorization, not on whether PHP decoded the value successfully.
Begin each feature by naming assets, actors, entry points, privileges, and failure impact. A public search endpoint, account update, administrator export, and background webhook have different threats. Security controls should follow the data flow and ownership boundaries rather than one generic sanitization function.
Use least privilege for database accounts, filesystem permissions, service credentials, and process users. Separate read-only from mutation capabilities where practical. A compromised endpoint should not automatically gain migration, shell, or unrestricted storage access.
Keep PHP, the web server, operating system, and enabled extensions on supported security releases. Disable unused extensions and services, inventory exposed entry points, and rehearse key rotation and emergency rollback before an incident.
Validation decides whether input belongs to the expected domain: type, length, range, format, allowed values, relationships, and authorization context. Normalize only after deciding what variants are accepted. Reject unexpected arrays when a scalar is required and cap collection sizes before expensive work.
Output encoding is context-specific. For HTML text and attribute values, use `htmlspecialchars` with an explicit character set and appropriate flags. JavaScript, CSS, URLs, HTTP headers, CSV, and shell arguments have different rules. Avoid inserting data into executable contexts when a structured API is available.
Escaping does not make a value safe for every destination, and validation does not replace encoding. An email address can be valid input and still need HTML encoding when displayed. Keep the raw validated domain value and encode at the final output boundary.
Rich HTML requires an allowlist sanitizer designed for HTML parsing; stripping tags is not a complete policy. Prefer plain text for user content unless rich formatting is an explicit maintained feature.
Use PDO prepared statements and bind untrusted values as parameters. Parameters represent data, not SQL syntax, so table names, column names, directions, and operators need an allowlisted mapping chosen by application code. Never concatenate request text into a query structure.
Configure error handling so database failures become controlled exceptions at the repository boundary. Do not reveal SQL, credentials, schema paths, or driver messages to clients. Log a redacted operation and correlation ID while preserving the original Throwable internally.
A prepared statement does not authorize a row. Every query must scope records to the current user, tenant, or permission as required. Recheck authorization inside the transaction when concurrent changes can invalidate an earlier decision.
Limit result size, pagination bounds, statement time, and transaction duration. Injection prevention is only one database security property; excessive queries, unbounded exports, and long locks can also become denial-of-service paths.
Store passwords with `password_hash` using an appropriate supported algorithm and verify them with `password_verify`. Let PHP manage salts, and use `password_needs_rehash` after successful login when policy changes. Rate-limit authentication and avoid revealing whether an account exists.
Regenerate the session identifier after authentication and privilege changes, invalidate sessions at logout or security events, and use secure cookie attributes. Session data on the server still needs authorization checks; possession of a session does not grant access to every record.
Protect state-changing cookie-authenticated requests with a CSRF token bound to the user session and verified with a timing-safe comparison where appropriate. SameSite cookies add defense but do not replace a token for every application and integration model.
Authorization belongs on the server for every action and object. Hide or disable UI controls for usability, then repeat the permission decision using trusted identity and current resource state. Test horizontal and vertical privilege changes by modifying identifiers and roles.
For uploads, enforce server-side size limits, upload status, expected media type from file content, generated storage names, and an allowlisted extension when it affects delivery. Store uploads outside executable public paths and serve them through an authorization-aware handler.
Do not build shell commands from request text. Prefer native PHP or library APIs. When process execution is unavoidable, use an API that separates executable and arguments where available, pass only allowlisted operations, use a restricted account, set time and output limits, and never rely on one escaping function as authorization.
Server-side requests can enable SSRF. Allowlist destinations or service identifiers, resolve and validate addresses under the network policy, restrict redirects and protocols, protect cloud metadata and internal networks, and bound response size and time. URL syntax validation alone is insufficient.
Canonicalize filesystem operations under an owned root and verify the resolved target stays there. Do not concatenate user paths into include, require, archive extraction, or deletion operations. Symlinks and race conditions need an ownership-aware design.
Load secrets from deployment-managed configuration with narrow access, not source files, logs, error pages, or browser-delivered code. Rotate keys, support overlap where protocols require it, and know which data must be re-encrypted or sessions revoked after compromise.
Use HTTPS and configure secure transport at the server or proxy. Set response headers from the application architecture: a restrictive Content Security Policy, frame protection, content-type sniffing protection, referrer policy, and caching rules for sensitive pages. Test the final headers after every proxy layer.
Security logs should record identity, operation, resource, decision, correlation ID, release, and safe failure category without passwords, tokens, session IDs, or sensitive payloads. Protect log access and retention because logs become another data store.
Test injection, output contexts, CSRF, identifier tampering, upload disguise, path traversal, SSRF, rate limits, session fixation, logout invalidation, and error redaction. Combine automated tests with dependency, configuration, and threat-model review before high-risk releases.
0 of 2 checked
Try this next
0 of 2 completed
Explore 500+ free tutorials across 20+ languages and frameworks.