phpsafe / guard
Zero-dependency security layer for PHP: request shield, static code scanner and safe helpers.
Requires
- php: >=8.1
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
PHP Safe Guard
A zero-dependency security layer for any PHP application.
Request shield · static code scanner · safe helpers
PHP Safe Guard is the open-source engine behind phpsafe.cloud. It gives an existing PHP site three things:
| Part | What it does |
|---|---|
| Shield | Inspects every request before your code runs. Blocks SQL injection, XSS, path traversal, code injection, executable uploads and known attack tools. Adds safe security headers and rate-limits login forms. |
| Scanner | Reads your source with PHP's own tokenizer (code is never executed) and reports risky lines with a fix for each one. Runs locally and in CI. |
| Helpers | Small, tested functions for CSRF tokens, context-aware escaping and safe file uploads. |
Install
With Composer:
composer require phpsafe/guard
Without Composer, download the latest release and copy the folder anywhere on your server. Nothing else is required: no extensions, no services, no database.
Shield
Add one line so Shield runs before every PHP file.
php.ini (or .user.ini on shared hosting):
auto_prepend_file = /path/to/phpsafe-guard/shield.php
Or at the very top of your front controller:
require __DIR__ . '/vendor/phpsafe/guard/shield.php';
That's it. Blocked visitors see a short page in their language (English, Korean, Chinese or Japanese) with an incident ID, and every event is written to guard.log as one JSON line.
Settings
Copy guard.config.example.php to guard.config.php next to shield.php, or point the PHPSAFE_GUARD_CONFIG environment variable to your own file. Every key is optional.
return [ 'mode' => 'block', // or 'log' to only record 'allow_params' => ['content'], // fields that legitimately contain HTML 'allow_paths' => ['/admin/editor'], // paths Shield skips 'login_paths' => ['/login'], // brute-force protection 'storage' => '/var/lib/phpsafe', // log + counters, outside the web root ];
Rolling out on an existing site: start with
'mode' => 'log'for a few days, readguard.log, add any rich-text fields toallow_params, then switch to'block'.
What Shield checks
| Family | Examples it stops |
|---|---|
| SQL injection | UNION SELECT, ' OR '1'='1, '--, stacked queries, SLEEP(), information_schema |
| XSS | <script>, event handlers like onerror=, javascript: URLs, <svg>, <iframe> |
| Path traversal | ../, /etc/passwd, php://filter, NUL bytes |
| Code injection | <?php in input, system(, shell pipes like ; cat, ${jndi: |
| Uploads | .php, .phtml, .phar, double extensions like cat.php.jpg, .htaccess, PHP code hidden inside images |
| Tools | sqlmap, nikto, wpscan, nuclei and other attack scanners |
Values are URL-decoded, entity-decoded and normalised before matching, so encoded payloads are caught too.
Scanner
vendor/bin/phpsafe scan # scan the current folder vendor/bin/phpsafe scan src --lang=ko vendor/bin/phpsafe scan . --format=json > report.json
CRITICAL SQL built with variables (SQL injection)
app/login.php:14 $db->query("SELECT * FROM users WHERE name = '$name'");
Fix: Use prepared statements: $db->prepare('... WHERE id = ?') then execute([$id]).
────────────────────────────────────────────────
Grade C 62/100 48 files · 5,210 lines
Critical 1 High 2 Medium 1 Low 0
| Option | Meaning |
|---|---|
--fail-on=high |
Exit code 1 when a finding of this level or worse exists (critical, high, medium, low, none). Use it to stop risky code in CI. |
--format=json |
Machine-readable output |
--exclude=a,b |
Extra folders or files to skip (vendor, node_modules, .git are always skipped) |
--lang=en|ko|zh|ja |
Message language |
Silence a reviewed line with a comment on the same line:
$config = require $file; // phpsafe-ignore: path comes from server config
GitHub Actions
- run: composer require --dev phpsafe/guard - run: vendor/bin/phpsafe scan src --fail-on=high
Rules
eval · shell execution · file inclusion from input · variable include paths · XSS · SQL built from variables · unserialize · extract on request data · preg_replace /e · file access from input · uploads saved with the original name · open redirect · md5/sha1 · predictable random numbers · phpinfo · display_errors turned on · SSL verification turned off · debug output of request data
Helpers
use PhpSafe\Guard\Csrf; use PhpSafe\Guard\Escape; use PhpSafe\Guard\Upload; // CSRF echo csrf_field(); // inside your <form> if (!Csrf::verify($_POST['_csrf'] ?? null)) { http_response_code(419); exit; } // Escaping echo e($name); // HTML text echo '<a href="' . Escape::safeUrl($link) . '">'; // blocks javascript: URLs echo '<script>const user = ' . Escape::js($user) . ';</script>'; // Uploads: real MIME check, random name, size limit, no hidden PHP $file = Upload::store($_FILES['avatar'], '/var/app-uploads', Upload::IMAGES, 2_000_000);
See examples/ for complete pages.
What Guard is, and what it is not
Guard is an extra layer of defense. Pattern matching catches the large majority of automated and copy-paste attacks, but it cannot understand your application's logic. It does not replace:
- prepared statements for every query
- escaping every value you print
- keeping PHP, your framework and dependencies updated (
composer audit)
Use it together with those basics, and with a full WAF such as ModSecurity + OWASP CRS on high-traffic sites.
Development
php tests/run.php # test suite, no dependencies needed php bin/phpsafe scan . --fail-on=low # Guard scans itself and must stay clean
Found a bypass or a false positive? Please read SECURITY.md for bypasses and open an issue for false positives.
License
MIT © PHP Safe — phpsafe.cloud