Hardening PHP-FPM with PHP-Snuffleupagus – Install and Configure on Debian/Ubuntu

PHP-Snuffleupagus is a PHP extension that adds a security layer to PHP-FPM — blocking SQL injection, XSS, dangerous function calls, and more at the interpreter level. It works with PHP 7.0 through 8.4 and is available via APT from the myguard repository.

Why use Snuffleupagus?

  • Blocks SQL injection, XSS, type juggling and other common PHP vulnerabilities at the interpreter level — before your application code even runs
  • Disables dangerous functions (system(), exec(), eval() etc.) globally or per virtual host
  • Detects and blocks request smuggling and serialization attacks
  • Defines custom per-site security policies — stricter than any WAF at the HTTP layer
  • Works alongside ModSecurity and NAXSI for defense in depth

Snuffleupagus vs ModSecurity: interpreter-level vs HTTP-level

ModSecurity and Snuffleupagus work at different layers of the stack and complement each other:

ModSecurity (nginx WAF) PHP-Snuffleupagus
Where it runs nginx process, inspects raw HTTP requests and responses PHP interpreter, inspects function calls and variable values inside PHP
What it can see HTTP headers, raw request body, URL, response body All PHP function arguments, return values, superglobals, file operations, cookie values
What it can block Malicious HTTP patterns (SQL in URL, XSS in form field) Dangerous PHP functions being called, type juggling tricks, deserialization of untrusted data
Encrypted payloads Cannot inspect encrypted body without TLS termination Always sees the decrypted, parsed PHP values
Bypassed by Encoding tricks, unusual HTTP patterns Very difficult to bypass: rules apply at the interpreter, not the network
Configuration Rule files (OWASP CRS) + nginx config .rules files per PHP-FPM pool

Run both: ModSecurity filters malicious HTTP traffic before PHP runs; Snuffleupagus enforces what PHP is allowed to do even if a payload gets through.

Installation

After adding the myguard repository:

apt-get update
apt-get install php8.3-snuffleupagus

Replace 8.3 with your PHP version (7.0 through 8.4 are supported).

Configuration

The main configuration file is created automatically at:

/etc/php/8.3/fpm/conf.d/snuffleupagus.ini

Edit it to enable rules:

nano /etc/php/8.3/fpm/conf.d/snuffleupagus.ini

The file references /etc/snuffleupagus/default.rules which contains the recommended rule set. Start with the defaults and tighten from there.

Verify the module is loaded

php-fpm8.3 -m | grep snuffleupagus

Restart PHP-FPM

systemctl restart php8.3-fpm

Key protections explained

Block dangerous PHP functions

Functions like system(), exec(), and passthru() execute OS commands directly. An attacker who finds a code injection vulnerability can use them to run arbitrary commands on the server. Snuffleupagus blocks them at the interpreter level — even if the attacker injects PHP code, calling these functions results in a logged drop, not execution.

# in your .rules file
sp.disable_function.function("system").drop();
sp.disable_function.function("exec").drop();
sp.disable_function.function("passthru").drop();
sp.disable_function.function("eval").drop();
sp.disable_function.function("shell_exec").drop();
sp.disable_function.function("proc_open").drop();

Enforce cookie security flags

Without the Secure flag, session cookies can be transmitted over HTTP, allowing interception. Without SameSite, cookies are sent on cross-site requests, enabling CSRF. Snuffleupagus enforces these flags on every cookie your PHP application sets, regardless of whether the application code sets them correctly.

sp.cookie.name("PHPSESSID").samesite("strict").secure();

Block upload of PHP files

File upload vulnerabilities let attackers upload a PHP file disguised as an image and then execute it. Snuffleupagus can validate uploads with a custom script before PHP processes them. The script receives the temporary file path and exits non-zero to reject the upload.

sp.upload_validation.script("/usr/bin/check-upload.sh").drop();

A simple check-upload.sh that rejects PHP files:

#!/bin/bash
file "$1" | grep -qi php && exit 1
exit 0

Block PHP deserialization of untrusted data

PHP’s unserialize() is a frequent attack vector — passing a crafted serialized string can instantiate arbitrary objects and trigger __wakeup() or __destruct() methods. If your application does not use unserialize() on user input, disable it:

sp.disable_function.function("unserialize").drop();

Virtual-host level rules

Snuffleupagus can apply different rule sets per PHP-FPM pool. This lets you apply strict rules to a legacy application without breaking a modern one on the same server. Add to your pool config:

; /etc/php/8.3/fpm/pool.d/myapp.conf
php_admin_value[snuffleupagus.config] = /etc/snuffleupagus/myapp.rules

Type juggling protection

PHP’s loose comparison (==) has well-known type juggling quirks: "0e12345" == "0" is true because both are treated as scientific notation. Attackers exploit this to bypass password hash comparisons. Snuffleupagus can enforce strict comparisons in sensitive contexts:

sp.type_juggling.enable();

Monitor the log

tail -f /var/log/snuffleupagus.log

Each blocked event is logged with the rule that triggered it, the function called, and the arguments. Use this to tune rules and identify false positives before switching from simulation mode to drop mode.

Frequently asked questions

Does Snuffleupagus work with WordPress?

Yes, with care. WordPress and its plugins use a broad set of PHP functions. Start with simulation_mode for each rule (which logs but does not block) to identify which rules trigger false positives. The Snuffleupagus documentation includes a WordPress-specific rule set as a starting point.

Can I run Snuffleupagus and ModSecurity at the same time?

Yes — they operate at different layers and do not interfere with each other. ModSecurity filters HTTP traffic before it reaches PHP; Snuffleupagus enforces rules inside the PHP process. Running both provides defense in depth.

Will blocking eval() break my application?

It depends. Many PHP applications (including WordPress) use eval() in template engines, plugin loaders, or obfuscated code. Use .simulation() mode first: sp.disable_function.function("eval").simulation(); — this logs calls to eval() without blocking them, so you can assess the impact.

Which PHP versions are supported?

PHP 7.0 through 8.4. Install the package matching your PHP version: php7.4-snuffleupagus, php8.1-snuffleupagus, etc. Multiple versions can coexist on the same system if you run multiple PHP-FPM versions.

Does Snuffleupagus affect PHP-FPM performance?

Minimally. Most rules are checked at function call time, not on every opcode. For a typical PHP-FPM application the overhead is under 1% of request time. The cookie and upload rules add negligible overhead.

Further reading