An extension for PHPStan that detects common security vulnerabilities in PHP code.
Its main feature is taint analysis: following user input ($_GET,
Symfony's and Laravel's Request objects and route parameters) through variables and function calls, and reporting when it reaches a
dangerous location such as a SQL query, a shell command, or echo.
Because it analyzes the whole project at once, it also catches input that a controller passes to a service or repository
in another file.
Warning
This extension is experimental and under active development. Error identifiers, configuration and behavior may change between releases. A clean run does not mean your code is free of vulnerabilities.
| Vulnerability | Identifier | Supported sinks |
|---|---|---|
| SQL injection | tainted.sql |
PDO, mysqli, SQLite3, Doctrine DBAL Connection, Laravel connections and query builders |
| Cross-site scripting (XSS) | tainted.html |
echo, print, printf(), vprintf() |
| Command injection | tainted.shell |
exec(), system(), shell_exec(), backticks, ... |
| Path traversal, SSRF | tainted.path |
file_get_contents(), fopen(), unlink(), ... |
| File inclusion | tainted.include |
include, include_once, require, require_once |
| Open redirect | tainted.redirect |
header() with a Location: or Refresh: header |
User input is detected from:
$_GET,$_POST,$_REQUEST,$_COOKIE- Symfony's
Requestand itsquery,request,cookiesandheadersbags - Laravel's
Request(input(),query(),all(),header(), ...) - Controller arguments that match a
{placeholder}in a#[Route]attribute
composer require --dev nth-root/phpstan-securityIf you use phpstan/extension-installer, that's all. Otherwise,
include the extension in your phpstan.neon:
includes:
- vendor/nth-root/phpstan-security/extension.neonTo ignore a single finding, use a @phpstan-ignore PHPDoc tag with the identifier:
/** @phpstan-ignore tainted.sql */
$connection->executeQuery($sql);Or ignore a whole category of findings with ignoreErrors in phpstan.neon:
parameters:
ignoreErrors:
- identifier: tainted.sqlThe analysis only follows tainted data through code it understands.
$_SERVERand$request->attributesare not detected as sources yet.- Built-in functions that aren't configured as a flow function stop taint.
That currently includes sanitizers like
htmlspecialchars()andescapeshellarg(). - Return values of user-defined functions and methods aren't followed yet.
- Values assigned to properties (
$this->query = $input) or array elements ($arr['q'] = $input) aren't followed yet. - The analysis is flow-insensitive: once a variable holds user input, it is considered tainted in that function, even if it is overwritten at a later point.
- Symfony route parameters are only treated as user input for routes defined with
#[Route]attributes. Other routing configuration (YAML, XML, PHP) is not read. - Twig templates aren't analyzed, so XSS through
{{ ... |raw }}isn't detected. - Laravel facade calls that are sinks or sources themselves (
DB::select(),Request::input()) aren't detected yet. - Laravel's
$request->name, therequest()helper, and methods called on$request->string()aren't followed yet. - Laravel route parameters from
routes/*.phparen't treated as user input. - Blade templates aren't analyzed, so XSS through
{!! ... !!}isn't detected.