psecio/iniscan
psecio/iniscan is a PHP security scanner that checks your php.ini configuration against recommended security settings. It highlights risky directives and helps harden production environments, making it useful for audits, CI checks, and server reviews.
Install as a dev dependency — it's a security linter, not runtime code:
composer require --dev psecio/iniscan
The package ships a CLI binary:
vendor/bin/iniscan scan
With no arguments it will scan the php.ini used by the current PHP binary (via php --ini). To target a specific file:
vendor/bin/iniscan scan --path=/etc/php/8.3/fpm/php.ini
First use case: run it against the exact php.ini that your FPM/CLI/Apache SAPI uses in production. This catches classic hardening gaps (display_errors=On, allow_url_include=On, weak session.cookie_httponly, exposed expose_php, dangerous disable_functions omissions) before they ship.
Look first at:
bin/iniscan in the vendor tree — the CLI entry point, and the best reference for invocation options.Psecio\Iniscan\Scan and Psecio\Iniscan\Rule — the two classes you'll touch if you embed it.CI/CD gate. Add it as a step in your pipeline, run against the environment's php.ini, and let the non-zero exit code fail the build when rules at or above a severity threshold trip. This is the highest-value pattern — cheap, fast, and catches config drift.
vendor/bin/iniscan scan --path=./php.ini --format=json
Machine-readable output for dashboards. --format=json (and xml) lets you feed results into a report step, Slack notifier, or a security dashboard rather than eyeballing console output. Parse the JSON and post only failed rules at severity >= your threshold.
Programmatic scanning — Artisan command wrapper. Wrap the library in a custom Artisan command so "scan this app's expected php.ini" is one memorable verb for the team:
$scan = new \Psecio\Iniscan\Scan(['path' => config('iniscan.path')]);
$results = $scan->run();
foreach ($results as $rule) {
if (!$rule->getResult()) { /* log severity, name, description */ }
}
(Exact accessors vary by version — inspect Rule in the vendor tree; getSeverity(), getResult(), and a description accessor are what you'll want.)
Environment matrix scan. Check every SAPI you run: FPM, CLI, and any Apache/mod_php or Octane worker context. They frequently diverge — CLI often has display_errors=On for dev convenience while FPM is hardened (or vice versa). Run the scan per-ini and diff results.
Docker build step. Run the scan inside your application image during build, against the image's own php.ini, so it audits exactly what ships rather than your host's config.
It reads files, not runtime state. Rules evaluate the php.ini text, not the effective ini_get() values after .htaccess, php_value, FPM pool php_admin_value, or ini_set() overrides. A "clean" scan can still be insecure at runtime if pool config weakens it — scan the FPM pool .conf separately.
Version-sensitive rules. Several checks only make sense for certain PHP major/minor versions (e.g. register_globals, magic_quotes, open_basedir semantics). Make sure it detects the right PHP version for the ini you're scanning; if in doubt, compare rules triggered across php -v variants.
Opinionated defaults. The rule set reflects general best practice, not your app's threat model. Some "failures" (e.g. allow_url_fopen) may be deliberately required by a dependency. Treat results as a conversation starter, not a hard mandate — tune your severity threshold accordingly.
Exit codes in CI. Confirm the exit status semantics for your version before wiring it into a pipeline gate — you want to fail only on rules above your chosen severity, otherwise you'll block merges on informational findings.
Console output is for humans. Don't grep the pretty console table; use JSON when you need to act on results. Column wrapping and ANSI color codes make the console format brittle to parse.
Extending rules. The rules are data-driven (JSON). You can typically point --rules at a custom file to add house-specific checks or relax defaults for your environment — useful for encoding internal hardening standards (e.g. "require open_basedir set to our docroot").
Pair with runtime checks. Static ini scanning doesn't catch runtime drift. Complement with a boot-time assertion or a middleware that logs ini_get('display_errors') etc., so container reconfigs can't silently regress after deploy.
Debugging a surprising pass/fail. Open the rules JSON and confirm the exact key, comparison operator, and expected value the rule uses before assuming a bug — most "wrong results" are a rule checking a directive that isn't present in your ini at all (which may silently pass or fail depending on the rule's default handling).
How can I help you explore Laravel packages today?