DevSecOps

PHPStan Security Analysis: Static Typing as a Security Tool for PHP

PHPStan brings static analysis to PHP. Its type checking catches entire classes of bugs that lead to security vulnerabilities in PHP applications.

Alex
Security Researcher
5 min read

PHP's reputation for security vulnerabilities is not entirely undeserved. The language's dynamic typing, implicit type coercion, and historically loose standard library have contributed to countless injection vulnerabilities, type juggling exploits, and logic errors. PHPStan changes the equation by bringing rigorous static type analysis to PHP without requiring a language change.

PHPStan analyzes PHP code without running it, catching type errors, null reference bugs, and logic issues that would otherwise surface only at runtime -- often in production, often in security-critical code paths.

Why Static Analysis Matters for PHP Security

PHP's type juggling is a well-known security hazard. The expression "0e123" == "0e456" evaluates to true in PHP because both strings are interpreted as zero in scientific notation when compared with the loose equality operator. This has been exploited in authentication bypasses where password hashes are compared with == instead of ===.

PHPStan at higher levels enforces strict type comparisons and catches patterns where loose comparison could lead to type juggling vulnerabilities.

Null reference prevention. PHPStan tracks nullable types through your code. A database query that might return null, an array lookup that might not find a key, a method that might return false instead of a string -- PHPStan forces you to handle these cases. In security contexts, unhandled null returns from authentication or authorization functions are a direct path to access control failures.

Dead code detection. Unreachable code in security logic is a red flag. If a validation check or authorization guard is unreachable, PHPStan will flag it. This catches refactoring mistakes where security code was accidentally bypassed.

Return type verification. PHPStan verifies that functions return what they declare. A function declared as returning bool that can return null in an edge case would be caught. If that function is an authorization check, the null return could be interpreted as falsy, granting unauthorized access.

PHPStan Levels

PHPStan has ten analysis levels (0-9), each stricter than the last. From a security perspective:

Level 0-2: Basic checks. Catches undefined variables, unknown classes and functions. Even this baseline catches issues that could lead to runtime errors in security paths.

Level 3-4: Return type verification and type checking for basic operations. Catches type mismatches that could lead to unexpected behavior.

Level 5-6: Argument type checking for function calls and method parameter validation. Catches cases where the wrong type is passed to security-sensitive functions.

Level 7-8: Union type handling and nullable type enforcement. This is where the security value increases significantly, as null handling bugs in authentication and authorization code are caught.

Level 9: The strictest level. Mixed type checking, strict property initialization, and dead code analysis. At this level, PHPStan catches almost every type-related bug that could have security implications.

Security-Focused Extensions

phpstan-strict-rules adds rules that enforce security-friendly coding patterns: disallowing loose comparison (==), requiring strict type declarations, and flagging dynamic property access.

phpstan-deprecation-rules flags use of deprecated PHP functions and classes. Deprecated functions are often deprecated precisely because they have security issues (like mysql_* functions or mcrypt_* functions).

Custom rules can be written for your specific security requirements. If your application has a sanitization function that must be called before rendering user input, you can write a PHPStan rule that enforces this pattern.

Practical Configuration

A security-focused phpstan.neon configuration:

parameters:
    level: 8
    paths:
        - src
    excludePaths:
        - src/legacy
    treatPhpDocTypesAsCertain: false
    reportUnmatchedIgnoredErrors: true
    
includes:
    - vendor/phpstan/phpstan-strict-rules/rules.neon

Setting treatPhpDocTypesAsCertain: false is important for security. PHPDoc types are not enforced at runtime, so trusting them entirely can mask real type mismatches. This setting makes PHPStan treat PHPDoc types as informational rather than authoritative.

Integration

Run PHPStan in CI as a required check. For existing projects, start at a lower level and increase incrementally. Use a baseline file to track existing issues:

phpstan analyse --generate-baseline

The baseline file records current findings so they do not block CI. New code must pass the configured level cleanly. This allows gradual adoption without requiring a massive cleanup before you can start benefiting from the tool.

Limitations

PHPStan does not perform taint analysis. It cannot trace user input from a request parameter through your code to a database query. For that, you need a dedicated taint analysis tool like Psalm (with its taint analysis plugin) or a commercial SAST tool.

PHPStan does not check for configuration-level security issues: insecure php.ini settings, missing security headers, or misconfigured session handling.

PHPStan does not scan dependencies for known vulnerabilities. That requires an SCA tool.

How Safeguard.sh Helps

Safeguard.sh provides the dependency security layer that PHPStan does not cover. While PHPStan catches type-related vulnerabilities in your PHP source code, Safeguard.sh monitors your Composer dependencies for known CVEs, generates SBOMs for your PHP projects, and alerts you when new vulnerabilities affect packages in your dependency tree. The combination gives you both code-level type safety and supply chain security coverage.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.