A PHP security scanner is a tool that analyzes your PHP source code, its Composer dependencies, or the running application to find security defects such as SQL injection, cross-site scripting, insecure deserialization, and known CVEs in third-party packages. No single scanner covers all of that, so most teams end up combining two or three tools. This post breaks down what each type actually inspects and how to wire them into a workflow that catches bugs before they ship.
PHP still runs a huge share of the web, and the language makes several classes of bug easy to introduce: string-concatenated SQL, eval() on request data, unserializing attacker input, and file-inclusion paths built from $_GET. A good PHP scanner is tuned to spot those patterns rather than treating PHP as generic text.
The three jobs a PHP scanner does
When people say "PHP security scanner" they usually mean one of three distinct things:
- Static application security testing (SAST) reads your source without running it and follows data from untrusted sources (request parameters, headers, files) to dangerous sinks (
mysqli_query,system,include). This is taint analysis, and it is how injection bugs get found. - Software composition analysis (SCA) looks at
composer.jsonandcomposer.lock, resolves the full dependency tree, and matches installed versions against vulnerability databases. Most of the code in a modern PHP app is vendored, so this is where a lot of real risk lives. - Dynamic testing (DAST) hits the running application over HTTP and probes endpoints for reflected XSS, injectable parameters, and misconfiguration. It sees what the deployed app actually exposes.
A useful mental model: SAST finds bugs in code you wrote, SCA finds bugs in code you imported, and DAST finds bugs in the app as deployed. You want coverage across all three.
Static analysis tools worth knowing
The most widely used open-source options are Psalm and PHPStan. Both are primarily type-checkers, but Psalm ships a taint-analysis mode (--taint-analysis) that traces user input to sinks and reports things like SQL injection and XSS with a real data-flow path. PHPStan pairs with community rule sets for similar coverage.
# Psalm taint analysis on an existing project
composer require --dev vimeo/psalm
vendor/bin/psalm --init
vendor/bin/psalm --taint-analysis
Progpilot is a dedicated PHP taint analyzer focused purely on security rather than type correctness, and Semgrep ships a large registry of PHP security rules that are quick to run in CI. For legacy code, RIPS was the classic dedicated PHP SAST engine, though it is now commercial and folded into a larger platform.
The honest limitation of any PHP SAST tool is dynamic language behavior. Variable variables ($$name), heavy use of magic methods, and runtime-built callable strings defeat static data-flow tracking, so expect both false negatives and false positives. Tune the rules to your framework and treat findings as leads, not verdicts.
Scanning your dependencies
Application code is only part of the attack surface. A typical Laravel or Symfony app pulls in dozens of transitive packages, and a vulnerable version buried three levels deep is just as exploitable as one you required directly. The built-in starting point is Composer itself:
# Flags installed packages with known advisories
composer audit
composer audit reads the PHP Security Advisories Database and reports any installed package with a published advisory. It is fast and free, but it only covers what is in that specific advisory feed and does not deduplicate or prioritize across a fleet of repos.
For broader coverage, a dedicated software composition analysis tool resolves the same lockfile against multiple vulnerability sources, follows transitive dependencies, and tells you the minimum version bump that clears a finding. An SCA tool such as Safeguard can also flag a vulnerable package that arrived transitively, which is the case composer audit output makes hardest to reason about because the offending package is not in your composer.json.
Keep composer.lock committed so the scanner sees exactly what deploys, and re-run the check on every pull request rather than once a quarter.
Adding dynamic scanning
SAST and SCA both work from files. To see what the running app exposes, point a DAST scanner at a deployed or staging instance. OWASP ZAP is the standard open-source choice: it spiders the application, then actively probes forms and parameters for reflected injection and misconfiguration.
Dynamic scanning catches things static tools miss, like a misconfigured header, a debug endpoint left enabled, or an injection that only manifests after several redirects. It also produces noise, so scope it to non-production first and review the DAST results before opening tickets. The two approaches are complementary: static analysis tells you where in the code a bug lives, dynamic testing confirms it is reachable from the outside.
Building a scanning workflow that sticks
Tools that only run manually get skipped. Wire the fast checks into CI so they run on every change:
# Minimal PHP security gate in CI
steps:
- run: composer install --no-progress
- run: composer audit
- run: vendor/bin/psalm --taint-analysis --no-cache
Fail the build on new high-severity findings, not on the entire existing backlog, or developers will learn to ignore the job. Baseline the current state, then hold the line so nothing worse gets merged. Run the heavier dynamic scan on a schedule against staging rather than on every commit.
A few practices that separate teams who get value from a PHP scanner from those who abandon it: assign an owner for triage so findings do not pile up unread, pin dependency versions so results are reproducible, and feed suppressions back into the tool config with a comment explaining why, so the next engineer understands the exception. If your team is new to this, the Safeguard Academy has walkthroughs on reading data-flow findings.
Choosing what to run
If you can only adopt one thing, start with dependency scanning. It has the highest signal-to-effort ratio because it finds real, exploitable CVEs in code you did not write and cannot fix by careful coding. Layer in Psalm or Semgrep taint analysis next to catch injection in your own code, and add ZAP against staging once the first two are stable. Any php scanner you pick should integrate with your CI and produce output your developers will actually read, because a report nobody opens fixes nothing.
FAQ
Is composer audit enough on its own?
It is a solid free baseline for known advisories in your dependencies, but it does not analyze your own source for injection flaws and it only covers the PHP advisory feed. Pair it with a static analyzer and, ideally, a broader SCA tool for transitive coverage and prioritization.
Can a PHP security scanner find SQL injection automatically?
Taint-analysis tools like Psalm and Progpilot can, when they successfully trace request data to a query sink. Dynamic PHP language features (variable variables, magic methods, runtime callables) reduce accuracy, so treat findings as leads to verify rather than confirmed exploits.
Do I need SAST, SCA, and DAST all at once?
Not on day one. Start with dependency scanning for the fastest return, add static taint analysis for your own code, and introduce dynamic scanning against staging once the first two run cleanly in CI.
How often should I run these scans?
Run dependency and static checks on every pull request so problems are caught before merge. Schedule dynamic scans against a staging environment weekly or nightly, since they are slower and noisier than the file-based checks.