UploadShield (Hardened v3)

This repository contains UploadShield (formerly "Upload Logger"): a hardened PHP upload protection helper. It provides a single-file monitor that logs uploads, detects common evasion techniques, quarantines suspicious files, and can optionally block malicious uploads.

Primary file: upload-logger.php

Summary

  • Purpose: Log normal uploads and raw-body uploads, detect double-extension tricks, fake images, PHP payloads embedded in files, and provide flood detection.
  • Runs only for HTTP requests; recommended to enable via auto_prepend_file in a per-site PHP-FPM pool for broad coverage.

Key configuration (top of upload-logger.php)

  • $logFile — path to the log file (default: __DIR__ . '/logs/uploads.log').
  • $BLOCK_SUSPICIOUS — if true the script returns 403 and exits when suspicious uploads are detected.
  • $MAX_SIZE — threshold for WARN big_upload (default 50 MB).
  • $RAW_BODY_MIN — minimum raw request body size considered suspicious when $_FILES is empty (default 500 KB).
  • $FLOOD_WINDOW_SEC, $FLOOD_MAX_UPLOADS — lightweight per-IP flood detection window and max uploads before alerting.
  • $SNIFF_MAX_BYTES, $SNIFF_MAX_FILESIZE — parameters controlling fast content sniffing for PHP/webshell markers.
  • $LOG_USER_AGENT — include User-Agent in logs when true.

What it detects

  • Dangerous filenames (path-traversal, double extensions, hidden php-like dotfiles).
  • Fake images: file extension indicates an image but finfo returns a non-image MIME.
  • PHP/webshell markers inside file content (fast head-scan up to configured limits).
  • Archive uploads (.zip, .tar, etc.) flagged for attention.
  • Raw request bodies (e.g., application/octet-stream or streamed uploads) when $_FILES is empty and body size exceeds $RAW_BODY_MIN.
  • Flooding by counting uploads per-IP in a rolling window.

Logging and alerts

  • Each accepted upload generates an UPLOAD line with fields: ip, user, name, size, type, real (detected MIME), tmp, uri, and optional ua.
  • Suspicious uploads generate ALERT suspicious entries with reasons= listing detected flags (e.g., bad_name,fake_image,php_payload).
  • Other notes: WARN big_upload, NOTE archive_upload, MULTIPART_NO_FILES, and RAW_BODY are emitted when appropriate.

Integration notes

  • Preferred deployment: set php_admin_value[auto_prepend_file] in the site-specific PHP-FPM pool to the absolute path of upload-logger.php so it runs before application code.
  • If using sessions for user identification, the script safely reads $_SESSION['user_id'] only when a session is active; do not rely on it being present unless your app starts sessions earlier.
  • The script uses is_uploaded_file()/finfo where available; ensure the PHP fileinfo extension is enabled for best MIME detection.
  • The script uses is_uploaded_file()/finfo where available; ensure the PHP fileinfo extension is enabled for best MIME detection.

Content detector & tuning

  • ContentDetector is now included and performs a fast head-scan of uploaded files to detect PHP open-tags and common webshell indicators (e.g., passthru(), system(), exec(), shell_exec(), proc_open(), popen(), base64_decode(), eval(), assert()).
  • The detector only scans the first N bytes of a file to limit CPU/io work; tune these limits in upload-logger.json:
    • limits.sniff_max_bytes — number of bytes to scan from file head (default 8192).
    • limits.sniff_max_filesize — only scan files up to this size in bytes (default 2097152 / 2MB).
  • Behavior note: eval() and similar tokens commonly appear inside SVG/JS contexts. The detector uses the detected MIME to be more permissive for XML/SVG-like content, but you should test and tune for your application's upload patterns to avoid false positives (see INTEGRATION.md).
  • If your application legitimately accepts encoded or templated payloads, add application-specific allowlist rules (URI or content-type) in allowlist.json or extend upload-logger.json with detector-specific tuning before enabling blocking mode. Further integration
  • Read the INTEGRATION.md for detector tuning, allowlists, and examples for log forwarding and Fail2Ban.
  • See docs/INSTALLATION.md for a step-by-step per-site install and auto_prepend_file examples.
  • Provision the required directories (quarantine, state) and set ownership/SELinux via the included provisioning script: scripts/provision_dirs.sh.
  • Example automation: scripts/ansible/upload-logger-provision.yml and scripts/systemd/upload-logger-provision.service are included as examples to run provisioning at deploy-time or boot.

Operational recommendations

  • Place the logs/ directory outside the webroot or deny web access to it.
  • Ensure correct owner/group and permissions (e.g., owner root, group www-data, chmod 750 on .security and chmod 640 for logs) and confirm PHP-FPM's user/group membership.
  • Rotate logs with logrotate (see INTEGRATION.md for an example snippet).
  • If your host uses SELinux/AppArmor, set correct contexts or adjust profiles so PHP-FPM can read the script and write logs.

Limitations & safety

  • This script improves visibility and blocks common upload tricks but cannot guarantee interception of every file-write vector (e.g., direct application writes, ZipArchive extraction, custom file APIs). Use it as part of a layered defense.
  • Content sniffing is limited to a head-scan to reduce CPU and false positives; tune $SNIFF_MAX_BYTES and $SNIFF_MAX_FILESIZE to balance coverage and performance.

Quick start

  1. Place upload-logger.php in a per-site secure folder (see INTEGRATION.md).
  2. Ensure the logs/ directory exists and is writable by PHP-FPM.
  3. Enable as an auto_prepend_file in the site pool and reload PHP-FPM.
  4. Monitor logs/uploads.log and adjust configuration options at the top of the script.

Support & changes

  • For changes, edit configuration variables at the top of upload-logger.php or adapt detection helpers as needed.

Generated for upload-logger.php (v3).

Additional documentation

Description
No description provided
Readme 96 KiB
Languages
PHP 89%
Shell 10.7%
PowerShell 0.3%