PHP Application Security
Security is a set of controls across input, output, identity, authorization, configuration, dependencies, and operations. No single function or firewall replaces secure application design.
On This Page
Prevent SQL and Command Injection
Use prepared statements for values and strict whitelists for identifiers. Avoid shell execution when a native PHP function or library exists.
<?php
declare(strict_types=1);
$stmt = $pdo->prepare('SELECT id FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
// When shell execution is unavoidable, pass each argument safely.
$command = sprintf(
'ping -c 1 -- %s',
escapeshellarg($validatedIpAddress)
);
Escape for the Output Context
<p><?= htmlspecialchars($customerName, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></p>
Do not place untrusted values directly into inline JavaScript, CSS, or raw HTML. Prefer data attributes encoded for HTML and parse them in external JavaScript.
Protect State-Changing Browser Requests
<?php
declare(strict_types=1);
$_SESSION['csrf_token'] ??= bin2hex(random_bytes(32));
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$submitted = $_POST['csrf_token'] ?? '';
if (!hash_equals($_SESSION['csrf_token'], $submitted)) {
http_response_code(419);
exit('The form expired. Reload the page and try again.');
}
}
Protect Authentication and Authorization
- Use
password_hash()andpassword_verify(). - Regenerate the session ID after login.
- Rate-limit authentication attempts.
- Require re-authentication for high-risk changes.
- Use role or permission checks at the server-side action boundary.
- Record privileged actions in an audit log.
Treat File Uploads as Hostile
<?php
declare(strict_types=1);
$file = $_FILES['document'] ?? null;
if (!is_array($file) || $file['error'] !== UPLOAD_ERR_OK) {
throw new RuntimeException('Upload failed.');
}
if ($file['size'] > 5 * 1024 * 1024) {
throw new RuntimeException('File exceeds the 5 MB limit.');
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
$allowed = ['application/pdf' => 'pdf', 'image/jpeg' => 'jpg'];
if (!isset($allowed[$mime])) {
throw new RuntimeException('Unsupported file type.');
}
$filename = bin2hex(random_bytes(16)) . '.' . $allowed[$mime];
$destination = dirname(__DIR__, 2) . '/storage/uploads/' . $filename;
if (!move_uploaded_file($file['tmp_name'], $destination)) {
throw new RuntimeException('Unable to store uploaded file.');
}
Configuration and Secrets
- Keep secrets out of source control and the public document root.
- Use separate credentials for development, staging, and production.
- Give the database account only the privileges the application needs.
- Rotate credentials after exposure and remove them from history.
- Never put secrets in URLs, client-side JavaScript, or logs.
Useful Response Headers
<?php
header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: strict-origin-when-cross-origin');
header('Permissions-Policy: camera=(), microphone=(), geolocation=()');
header("Content-Security-Policy: default-src 'self'; frame-ancestors 'none'; base-uri 'self'");
Build a Content Security Policy around the assets the application actually needs. Test it in report-only mode before enforcement on an existing site.
Log Security-Relevant Events
Record login success and failure, permission denials, password changes, user and role changes, token creation, data exports, and administrative actions. Include actor, target, time, request correlation ID, and outcome. Avoid passwords, session IDs, API keys, full payment data, and unnecessary personal information.