Find every insecure PHP setting without running PHP

php-ini-audit reads php.ini, conf.d, PHP-FPM pools, .user.ini and .htaccess, rebuilds the effective configuration the way PHP does, and reports each matching rule with its severity, confidence and the exact file and line it came from.

Quick start #

composer global require jorgsowa/php-ini-audit
php-ini-audit                              # discover and audit this host
php-ini-audit --root /mnt/rootfs           # audit an extracted container image
php-ini-audit /etc/php/8.3/fpm/php.ini     # audit one installation

What a report looks like #

Findings are grouped per effective configuration: a php.ini with its scan directory, and every overlay on top of it (FPM pools, .user.ini, [PATH=] sections).

== /etc/php/8.2/fpm/php.ini  [sapi fpm, php 8.2]
   files: /etc/php/8.2/fpm/php.ini, /etc/php/8.2/fpm/conf.d/20-xdebug.ini, …
  CRITICAL EXEC-001  Remote file inclusion enabled  (confidence high)
           allow_url_include is enabled
           at /etc/php/8.2/fpm/php.ini:22
           fix: Set allow_url_include = Off.
  HIGH     SESS-002  Session IDs accepted outside cookies  (confidence high)
           session.use_only_cookies is Off
           at /etc/php/8.2/fpm/php.ini:35
  LOW      ERR-004   PHP version disclosed in HTTP headers  (confidence high)
           expose_php is On
           at built-in default

== /etc/php/8.2/fpm/php.ini + pool [www] (/etc/php/8.2/fpm/pool.d/www.conf)
  CRITICAL FPM-001   FastCGI port reachable from the network  (confidence medium)
           listen = 0.0.0.0:9000 binds every interface without listen.allowed_clients
           at /etc/php/8.2/fpm/pool.d/www.conf:4

Design principles #

Never executes anything

No php -i, no subprocesses, no eval, no network. A test fails the build if such a call is added.

Not a pass/fail verdict

Every matching rule is reported, each with its own severity and confidence. One rule can report several problems.

Defaults count

Unset directives are checked against PHP's built-in defaults, which is where Docker images without a php.ini go wrong.

Knows where values come from

Every finding points at the file and line that set the effective value, or says it is a default.

Understands overlays

FPM php_admin_value, .user.ini and .htaccess are layered with PHP's changeability rules, so ignored hardening is reported.

Known CVEs, in context

CVEs are matched to the exact PHP version and reported only when the configuration actually uses the vulnerable feature, such as php-cgi on Windows or allow_url_fopen. Every other publicly disclosed CVE for that version is listed in one warning.

Checks referenced paths

Session, upload, log, extension and CA paths are checked for world-writable permissions and for sitting inside the web root, using stat() only.

Coverage #

PrefixCategoryRules
EXECCode execution17
FSFilesystem and paths11
ERRInformation disclosure11
SESSSession security14
RESResource limits and DoS11
CGICGI / FastCGI2
EXTDebug and profiling extensions3
OPCOPcache3
DBDatabase and credentials3
ENCCharacter encoding4
CFGConfiguration hygiene10
FPMPHP-FPM pools8
HTAApache .htaccess1
VULNPHP version support3
CVEKnown vulnerabilities29