PHP security is not a separate discipline—it is a set of practices woven into how you write, test, and deploy code. The OWASP Top 10 has not changed dramatically in years because the fundamentals remain the same: validate input, encode output, use parameterized queries, and manage authentication properly. What has changed is the tooling available to enforce these practices automatically.
Content Security Policy (CSP)
CSP is an HTTP header that tells browsers which resources (scripts, styles, images, fonts) are allowed to load. It is the most effective defense against cross-site scripting (XSS) attacks.
Basic CSP for PHP applications
1 | // Middleware or bootstrap |
Nonce-based CSP for inline scripts
If your PHP templates include inline <script> blocks, use nonces:
1 | // Generate a unique nonce per request |
1 | <!-- In your template --> |
CSP report-only mode
Deploy CSP in report-only mode first to catch violations without breaking functionality:
1 | header("Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report"); |
1 | // /csp-report endpoint |
Static Application Security Testing (SAST)
SAST tools analyze your source code without running it. They catch security issues before the code reaches production.
Psalm with security analysis
1 | composer require --dev vimeo/psalm |
Psalm’s taint analysis tracks data flow from user input to sensitive operations:
1 | // Psalm catches this: tainted input reaches SQL query |
PHPStan security rules
1 | composer require --dev phpstan/phpstan-strict-rules |
1 | # phpstan.neon |
PHPStan catches type-related security issues: loose comparisons, missing null checks, and type juggling vulnerabilities.
Snyk and Semgrep
For larger projects, dedicated SAST tools provide broader coverage:
1 | # Snyk scans dependencies for known vulnerabilities |
Input validation
Every piece of data from outside your application is untrusted: GET/POST parameters, headers, cookies, uploaded files, and API request bodies.
Validation patterns
1 | // Laravel validation (preferred in Laravel projects) |
Common validation mistakes
1 | // BAD: trusting filter_var alone for URLs |
Output encoding
Every variable rendered in HTML must be encoded:
1 | // In Blade templates (auto-escaped) |
Context-specific encoding
Different contexts need different encoding:
1 | // HTML context |
Authentication hardening
Password hashing
1 | // Always use password_hash with the default algorithm |
PASSWORD_DEFAULT uses bcrypt currently and will automatically upgrade to stronger algorithms in future PHP versions. Never use md5(), sha1(), or sha256() for passwords.
Rate limiting authentication
1 | // Simple rate limiter using cache |
Timing-safe comparison
1 | // WRONG: vulnerable to timing attacks |
Security headers
1 | // Complete security headers for a PHP application |
Common mistakes
Using == instead of === for security checks: PHP’s loose comparison causes type juggling. "0" == false is true. Always use strict comparison.
Storing secrets in version control: API keys, database passwords, and encryption keys belong in environment variables, not in .env files committed to git.
Not updating dependencies: Run composer audit regularly. Known vulnerabilities in dependencies are the lowest-effort attack vector.
1 | # Check for known vulnerabilities in dependencies |
Trusting client-side validation: JavaScript validation is a UX improvement, not a security measure. Always re-validate on the server.
FAQ
Is PHP inherently less secure than other languages?
No. PHP’s security reputation comes from its low barrier to entry (many beginners write insecure code) and historical design choices (like register_globals, removed long ago). Modern PHP with frameworks like Laravel has security primitives that match any language.
Should I use a WAF (Web Application Firewall)?
A WAF adds defense-in-depth but does not replace secure code. Use Cloudflare WAF or ModSecurity as an additional layer, not as your primary defense.
How often should I run security scans?
Run SAST on every pull request. Run dependency audits weekly. Run penetration tests quarterly for high-value applications.
Next steps
Start with two actions: add composer audit to your CI pipeline and deploy CSP in report-only mode. These two steps catch the most common vulnerability categories with minimal effort.
For understanding how PHP handles data at the language level, the PDO prepared statements guide covers the database security fundamentals. The Docker guide covers container-level security hardening that complements application-level security.