PHP security best practices are still essential for modern PHP 8 applications, Laravel and Symfony services, WordPress-adjacent systems, and internal business tools. Secure PHP means validating input, encoding output, using prepared statements, hardening sessions, securing file uploads, protecting secrets, and scanning code and Composer dependencies before release.
This guide is designed to outperform fragmented PHP security references by giving engineering teams a practical checklist with examples they can use in code review.
PHP security checklist
Start with these controls:
- Run a supported PHP version and patch quickly.
- Validate all request input on the server.
- Encode output for the correct context: HTML, JavaScript, URL, or attribute.
- Use PDO prepared statements or framework query builders.
- Hash passwords with
password_hash()and verify withpassword_verify(). - Harden sessions and cookies.
- Protect state-changing forms from CSRF.
- Secure file uploads with type, size, storage, and scanning controls.
- Avoid dangerous dynamic execution and unsafe deserialization.
- Keep Composer packages updated and remove unused dependencies.
- Keep secrets out of PHP files,
.envfiles committed to Git, and logs. - Scan PHP code and dependencies in pull requests.
Corgea AI SAST helps enforce code-level PHP security patterns, and Corgea dependency scanning helps prioritize Composer package risk.
1. Validate input and encode output
Every value from $_GET, $_POST, $_COOKIE, $_FILES, headers, JSON bodies, and route parameters is untrusted. Validate type, length, format, and allowed values before business logic uses it.
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
if ($email === false) {
http_response_code(400);
exit('Invalid request');
}
Validation prevents malformed data from entering the application. Output encoding prevents untrusted data from becoming executable content.
<p><?= htmlspecialchars($displayName, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></p>
Use framework escaping helpers when available. Avoid mixing raw user-generated HTML into templates unless it has gone through a strict allowlist sanitizer.
2. Prevent SQL injection with PDO prepared statements
String-concatenated SQL is one of the fastest ways to create a serious PHP vulnerability. Use prepared statements and bound parameters.
$stmt = $pdo->prepare(
'SELECT id, email FROM users WHERE email = :email AND organization_id = :organization_id'
);
$stmt->execute([
'email' => $email,
'organization_id' => $organizationId,
]);
$user = $stmt->fetch();
Framework query builders and ORMs can help, but raw SQL paths still need review. Read Corgea’s SQL injection guide for the underlying attack pattern.
3. Harden PHP sessions and cookies
PHP session security best practices focus on preventing session theft, fixation, and unnecessary exposure.
session_set_cookie_params([
'lifetime' => 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
session_regenerate_id(true);
Use these practices:
- Regenerate the session ID after login and privilege changes.
- Use HTTPS everywhere.
- Set
HttpOnly,Secure, andSameSite. - Keep session payloads small and non-sensitive.
- Expire inactive sessions.
- Store server-side session data somewhere appropriate for your scale and threat model.
4. Secure PHP file uploads
PHP file upload security best practices deserve their own review because upload endpoints combine authentication, storage, parsing, and often public delivery.
Do not trust the original filename or browser-provided content type.
if ($_FILES['document']['error'] !== UPLOAD_ERR_OK) {
http_response_code(400);
exit('Upload failed');
}
if ($_FILES['document']['size'] > 5 * 1024 * 1024) {
http_response_code(400);
exit('File too large');
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($_FILES['document']['tmp_name']);
if ($mime !== 'application/pdf') {
http_response_code(400);
exit('Invalid file type');
}
$name = bin2hex(random_bytes(16)) . '.pdf';
$destination = '/srv/private-uploads/' . $name;
move_uploaded_file($_FILES['document']['tmp_name'], $destination);
Store uploads outside the web root or in private object storage. If the file must become public, serve it through an authorization-aware download route or a short-lived signed URL.
5. Hash passwords correctly
Never store plaintext passwords or fast hashes such as unsalted MD5/SHA1.
$hash = password_hash($password, PASSWORD_DEFAULT);
if (!password_verify($passwordAttempt, $hash)) {
http_response_code(401);
exit('Invalid credentials');
}
PASSWORD_DEFAULT tracks PHP’s recommended default algorithm over time. For high-risk applications, tune cost settings and monitor login latency.
6. Protect against CSRF and XSS
If your PHP app uses cookies for authentication, state-changing requests should use CSRF tokens. Generate a token server-side, store it in the session, place it in the form, and verify it on submit.
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
http_response_code(403);
exit('Invalid CSRF token');
}
For XSS, combine output encoding, template escaping, safe Markdown/rich text handling, and security headers. Content Security Policy can reduce impact when an encoding bug slips through.
7. Avoid dangerous functions and unsafe deserialization
Review use of:
evalassertwith stringssystem,exec,passthru, and shell wrappersincludeorrequirewith dynamic pathsunserializeon untrusted data
If you must call shell commands, pass only validated values and use escaping functions carefully. Prefer APIs that avoid shell interpretation entirely.
For deserialization, prefer JSON DTOs over PHP object deserialization from untrusted sources. If legacy code uses unserialize, migrate or strictly restrict allowed classes.
8. Manage Composer dependency risk
Composer packages are part of your application attack surface. Apply the same discipline you use for source code:
- Commit
composer.lock. - Remove unused packages.
- Monitor advisories and patch promptly.
- Review abandoned packages.
- Prefer maintained libraries with clear provenance.
- Separate dev dependencies from production deployments.
Corgea dependency scanning helps PHP teams prioritize vulnerable Composer packages based on real usage and remediation impact.
9. Protect secrets and configuration
.env files are useful, but they are not magic. Do not commit them. Do not print them in error pages. Do not bake them into Docker images.
Use:
- a managed secret store in production;
- environment-specific configuration;
- Corgea secrets scanning in pull requests;
- least-privilege database and API credentials;
- safe logging defaults that redact tokens and PII.
10. Laravel and Symfony security notes
Laravel and Symfony provide strong security primitives, but only if teams use them consistently.
For Laravel:
- use form requests or validators;
- rely on Eloquent parameterization;
- keep CSRF middleware enabled for web routes;
- use policies/gates for authorization;
- avoid mass assignment mistakes with guarded/fillable models.
For Symfony:
- use validators and form components;
- keep Twig auto-escaping enabled;
- use voters for authorization;
- configure secure cookies and trusted proxies correctly;
- avoid exposing debug mode in production.
Frameworks reduce boilerplate. They do not remove the need for review and scanning.
Try Corgea scanning for PHP
Want PHP security best practices enforced in every pull request? Try Corgea for PHP applications:
- Corgea AI SAST scans PHP code for injection, unsafe data flow, insecure API usage, and authorization mistakes.
- Corgea dependency scanning prioritizes Composer package risk.
- Corgea secrets scanning helps stop leaked credentials before they spread.
- Corgea developer experience delivers findings and fix guidance where developers review code.
Try Corgea today or book a custom demo for your PHP portfolio.
If you are comparing scanners, start with Corgea vs Snyk, Corgea vs Semgrep, and Corgea vs Checkmarx. For GitHub-native teams, Corgea vs GitHub Advanced Security is also useful.
FAQ
What are the most important PHP security best practices?
The most important PHP security best practices are validating input, encoding output, using PDO prepared statements, hardening sessions and cookies, securing file uploads, hashing passwords with password_hash, keeping Composer dependencies updated, and scanning code before merge.
How do you secure PHP sessions?
Use secure cookie flags, regenerate session IDs after login and privilege changes, set appropriate idle timeouts, avoid storing sensitive data in the session, and ensure sessions are only sent over HTTPS.
How do you secure PHP file uploads?
Authenticate upload routes, enforce size limits, validate MIME type and file signatures, generate server-side filenames, store files outside the web root or in private object storage, and scan uploaded content before processing.
Can Corgea scan PHP applications?
Yes. Corgea AI SAST helps scan PHP code for code-level vulnerabilities, and Corgea dependency scanning helps prioritize vulnerable Composer packages in pull requests and CI/CD.