Rename project to UploadShield: update runtime, configs, docs, and provisioning; run lint/tests
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -8,7 +8,7 @@
|
|||||||
uploads.log
|
uploads.log
|
||||||
|
|
||||||
# Peek allow marker (local only)
|
# Peek allow marker (local only)
|
||||||
/.upload_logger_allow_peek
|
/.uploadshield_allow_peek
|
||||||
|
|
||||||
# Local environment files
|
# Local environment files
|
||||||
.env
|
.env
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Configuration Reference
|
# Configuration Reference
|
||||||
|
|
||||||
This file maps the top-level configuration keys used by `upload-logger.json` (UploadShield) to their effect and defaults. Use absolute paths in production where possible.
|
This file maps the top-level configuration keys used by `uploadshield.json` (UploadShield) to their effect and defaults. Use absolute paths in production where possible.
|
||||||
|
|
||||||
## Top-level sections
|
## Top-level sections
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ This file maps the top-level configuration keys used by `upload-logger.json` (Up
|
|||||||
- `allow_xml_eval` (bool) — relax `eval()` detection for XML/SVG content when true.
|
- `allow_xml_eval` (bool) — relax `eval()` detection for XML/SVG content when true.
|
||||||
- `custom_patterns` (array) — array of PCRE patterns (as strings) applied to the file head. Invalid patterns are ignored.
|
- `custom_patterns` (array) — array of PCRE patterns (as strings) applied to the file head. Invalid patterns are ignored.
|
||||||
|
|
||||||
## Example `upload-logger.json`
|
## Example `uploadshield.json`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -67,6 +67,6 @@ This file maps the top-level configuration keys used by `upload-logger.json` (Up
|
|||||||
- Use absolute paths in `paths.*` when deploying under systemd/Ansible to avoid cwd surprises.
|
- Use absolute paths in `paths.*` when deploying under systemd/Ansible to avoid cwd surprises.
|
||||||
- Ensure `quarantine_dir` is inaccessible from the web and set to owner `root` and mode `0700`; files inside should be `0600`.
|
- Ensure `quarantine_dir` is inaccessible from the web and set to owner `root` and mode `0700`; files inside should be `0600`.
|
||||||
|
|
||||||
If you want, I can generate a per-site `upload-logger.json` filled with your preferred absolute paths and ownership values.
|
If you want, I can generate a per-site `uploadshield.json` filled with your preferred absolute paths and ownership values.
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ Add CONFIG_REFERENCE.md mapping configuration options
|
|||||||
- A basic smoke harness exists under `tests/smoke/`. To run locally:
|
- A basic smoke harness exists under `tests/smoke/`. To run locally:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
php -S 127.0.0.1:8000 -t tests/smoke/public -d auto_prepend_file=$(pwd)/upload-logger.php
|
php -S 127.0.0.1:8000 -t tests/smoke/public -d auto_prepend_file=$(pwd)/uploadshield.php
|
||||||
# then POST files with curl or a test client
|
# then POST files with curl or a test client
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
This document complements the installation steps in [docs/INSTALLATION.md](docs/INSTALLATION.md) by focusing on detector tuning, allowlists, and advanced integrations (log forwarding, Fail2Ban, etc.).
|
This document complements the installation steps in [docs/INSTALLATION.md](docs/INSTALLATION.md) by focusing on detector tuning, allowlists, and advanced integrations (log forwarding, Fail2Ban, etc.).
|
||||||
|
|
||||||
Example `upload-logger.json` (simplified) for UploadShield:
|
Example `uploadshield.json` (simplified) for UploadShield:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -50,7 +50,7 @@ Notes:
|
|||||||
### Content detector tuning
|
### Content detector tuning
|
||||||
|
|
||||||
- The `ContentDetector` performs a fast head-scan to detect PHP open-tags and common webshell indicators (e.g., `passthru`, `system`, `exec`, `base64_decode`, `eval`, `assert`).
|
- The `ContentDetector` performs a fast head-scan to detect PHP open-tags and common webshell indicators (e.g., `passthru`, `system`, `exec`, `base64_decode`, `eval`, `assert`).
|
||||||
- Tuning options (in `upload-logger.json`):
|
- Tuning options (in `uploadshield.json`):
|
||||||
- `limits.sniff_max_bytes` (default 8192) — how many bytes to scan from the file head.
|
- `limits.sniff_max_bytes` (default 8192) — how many bytes to scan from the file head.
|
||||||
- `limits.sniff_max_filesize` (default 2097152) — only scan files up to this size.
|
- `limits.sniff_max_filesize` (default 2097152) — only scan files up to this size.
|
||||||
- `detectors.content.allow_xml_eval` — relax `eval()` detection for XML/SVG when appropriate.
|
- `detectors.content.allow_xml_eval` — relax `eval()` detection for XML/SVG when appropriate.
|
||||||
@@ -110,14 +110,14 @@ filebeat.inputs:
|
|||||||
json.keys_under_root: true
|
json.keys_under_root: true
|
||||||
json.add_error_key: true
|
json.add_error_key: true
|
||||||
fields:
|
fields:
|
||||||
source: php-upload-logger
|
source: php-uploadshield
|
||||||
output.logstash:
|
output.logstash:
|
||||||
hosts: ["logserver:5044"]
|
hosts: ["logserver:5044"]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Logrotate & SELinux notes
|
## Logrotate & SELinux notes
|
||||||
|
|
||||||
Per-site `logrotate` snippets are included in `examples/logrotate.d/upload-logger`. Use `copytruncate` or reload PHP-FPM after rotation.
|
Per-site `logrotate` snippets are included in `examples/logrotate.d/uploadshield`. Use `copytruncate` or reload PHP-FPM after rotation.
|
||||||
|
|
||||||
If SELinux is enabled, the provisioning script attempts to register fcontexts and run `restorecon`. Verify contexts manually as needed.
|
If SELinux is enabled, the provisioning script attempts to register fcontexts and run `restorecon`. Verify contexts manually as needed.
|
||||||
|
|
||||||
@@ -125,6 +125,6 @@ If SELinux is enabled, the provisioning script attempts to register fcontexts an
|
|||||||
|
|
||||||
- Use observe mode (`ops.block_suspicious: false`) while tuning.
|
- Use observe mode (`ops.block_suspicious: false`) while tuning.
|
||||||
- After tuning, enable blocking in a controlled rollout (canary hosts first).
|
- After tuning, enable blocking in a controlled rollout (canary hosts first).
|
||||||
- Keep `upload-logger.php` and `.security` owned by `root` and ensure logs and quarantine are not web-accessible.
|
- Keep `uploadshield.php` and `.security` owned by `root` and ensure logs and quarantine are not web-accessible.
|
||||||
|
|
||||||
For installation steps and per-site configuration, see `docs/INSTALLATION.md`.
|
For installation steps and per-site configuration, see `docs/INSTALLATION.md`.
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
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.
|
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](upload-logger.php)
|
Primary file: [uploadshield.php](uploadshield.php)
|
||||||
|
|
||||||
Summary
|
Summary
|
||||||
- Purpose: Log normal uploads and raw-body uploads, detect double-extension tricks, fake images, PHP payloads embedded in files, and provide flood detection.
|
- 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.
|
- 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`)
|
Key configuration (top of `uploadshield.php`)
|
||||||
- `$logFile` — path to the log file (default: `__DIR__ . '/logs/uploads.log'`).
|
- `$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.
|
- `$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).
|
- `$MAX_SIZE` — threshold for `WARN big_upload` (default 50 MB).
|
||||||
@@ -31,7 +31,7 @@ Logging and alerts
|
|||||||
- Other notes: `WARN big_upload`, `NOTE archive_upload`, `MULTIPART_NO_FILES`, and `RAW_BODY` are emitted when appropriate.
|
- Other notes: `WARN big_upload`, `NOTE archive_upload`, `MULTIPART_NO_FILES`, and `RAW_BODY` are emitted when appropriate.
|
||||||
|
|
||||||
Integration notes
|
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.
|
- Preferred deployment: set `php_admin_value[auto_prepend_file]` in the site-specific PHP-FPM pool to the absolute path of `uploadshield.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.
|
- 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.
|
||||||
- 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.
|
||||||
@@ -39,16 +39,16 @@ Integration notes
|
|||||||
Content detector & tuning
|
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()`).
|
- `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`:
|
- The detector only scans the first N bytes of a file to limit CPU/io work; tune these limits in `uploadshield.json`:
|
||||||
- `limits.sniff_max_bytes` — number of bytes to scan from file head (default `8192`).
|
- `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).
|
- `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`).
|
- 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.
|
If your application legitimately accepts encoded or templated payloads, add application-specific allowlist rules (URI or content-type) in `allowlist.json` or extend `uploadshield.json` with detector-specific tuning before enabling blocking mode.
|
||||||
Further integration
|
Further integration
|
||||||
- Read the `INTEGRATION.md` for detector tuning, allowlists, and examples for log forwarding and Fail2Ban.
|
- 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.
|
- 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`.
|
- 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.
|
- Example automation: `scripts/ansible/uploadshield-provision.yml` and `scripts/systemd/uploadshield-provision.service` are included as examples to run provisioning at deploy-time or boot.
|
||||||
|
|
||||||
Operational recommendations
|
Operational recommendations
|
||||||
- Place the `logs/` directory outside the webroot or deny web access to it.
|
- Place the `logs/` directory outside the webroot or deny web access to it.
|
||||||
@@ -61,16 +61,16 @@ Limitations & safety
|
|||||||
- 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.
|
- 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
|
Quick start
|
||||||
1. Place `upload-logger.php` in a per-site secure folder (see `INTEGRATION.md`).
|
1. Place `uploadshield.php` in a per-site secure folder (see `INTEGRATION.md`).
|
||||||
2. Ensure the `logs/` directory exists and is writable by PHP-FPM.
|
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.
|
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.
|
4. Monitor `logs/uploads.log` and adjust configuration options at the top of the script.
|
||||||
|
|
||||||
Support & changes
|
Support & changes
|
||||||
- For changes, edit configuration variables at the top of `upload-logger.php` or adapt detection helpers as needed.
|
- For changes, edit configuration variables at the top of `uploadshield.php` or adapt detection helpers as needed.
|
||||||
|
|
||||||
---
|
---
|
||||||
Generated for upload-logger.php (UploadShield v3).
|
Generated for uploadshield.php (UploadShield v3).
|
||||||
|
|
||||||
## Additional documentation
|
## Additional documentation
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
"archive_inspect": true
|
"archive_inspect": true
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"quarantine_dir": "/var/lib/upload-logger/quarantine",
|
"quarantine_dir": "/var/lib/uploadshield/quarantine",
|
||||||
"state_dir": "/var/lib/upload-logger/state",
|
"state_dir": "/var/lib/uploadshield/state",
|
||||||
"allowlist_file": "/etc/upload-logger/allowlist.json"
|
"allowlist_file": "/etc/uploadshield/allowlist.json"
|
||||||
},
|
},
|
||||||
"limits": {
|
"limits": {
|
||||||
"max_size": 52428800,
|
"max_size": 52428800,
|
||||||
@@ -7,9 +7,9 @@
|
|||||||
"archive_inspect": true
|
"archive_inspect": true
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"quarantine_dir": "/var/lib/upload-logger/quarantine",
|
"quarantine_dir": "/var/lib/uploadshield/quarantine",
|
||||||
"state_dir": "/var/lib/upload-logger/state",
|
"state_dir": "/var/lib/uploadshield/state",
|
||||||
"allowlist_file": "/etc/upload-logger/allowlist.json"
|
"allowlist_file": "/etc/uploadshield/allowlist.json"
|
||||||
},
|
},
|
||||||
"limits": {
|
"limits": {
|
||||||
"max_size": 52428800,
|
"max_size": 52428800,
|
||||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace UploadLogger\Core;
|
namespace UploadLogger\Core;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple immutable configuration holder for UploadShield (upload-logger.json).
|
* Simple immutable configuration holder for UploadShield (uploadshield.json).
|
||||||
*/
|
*/
|
||||||
final class Config
|
final class Config
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Installation & Production Deployment Guide
|
# Installation & Production Deployment Guide
|
||||||
|
|
||||||
This guide shows a minimal, secure installation and rollout path for UploadShield's primary script (`upload-logger.php`).
|
This guide shows a minimal, secure installation and rollout path for UploadShield's primary script (`uploadshield.php`).
|
||||||
Follow these steps in a staging environment first; do not enable blocking until detectors are tuned.
|
Follow these steps in a staging environment first; do not enable blocking until detectors are tuned.
|
||||||
|
|
||||||
**Prerequisites**
|
**Prerequisites**
|
||||||
@@ -9,9 +9,9 @@ Follow these steps in a staging environment first; do not enable blocking until
|
|||||||
- SSH/privileged access to configure the site pool and run provisioning scripts.
|
- SSH/privileged access to configure the site pool and run provisioning scripts.
|
||||||
|
|
||||||
**Quick overview**
|
**Quick overview**
|
||||||
1. Place `upload-logger.php` in a secure per-site folder (recommended `.security`).
|
1. Place `uploadshield.php` in a secure per-site folder (recommended `.security`).
|
||||||
2. Create `logs/`, `quarantine/`, `state/` and set strict ownership and permissions.
|
2. Create `logs/`, `quarantine/`, `state/` and set strict ownership and permissions.
|
||||||
3. Configure `upload-logger.json` for your environment; keep `ops.block_suspicious` off for initial tuning.
|
3. Configure `uploadshield.json` for your environment; keep `ops.block_suspicious` off for initial tuning.
|
||||||
4. Enable `auto_prepend_file` in the site PHP-FPM pool to run the logger before application code.
|
4. Enable `auto_prepend_file` in the site PHP-FPM pool to run the logger before application code.
|
||||||
5. Verify logging, tune detectors, deploy log rotation, and enable alerting.
|
5. Verify logging, tune detectors, deploy log rotation, and enable alerting.
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ vendor/bin/phpstan analyse -c phpstan.neon
|
|||||||
```
|
```
|
||||||
|
|
||||||
**2. Recommended file layout (per-site)**
|
**2. Recommended file layout (per-site)**
|
||||||
Use a hidden per-site security folder so `upload-logger.php` is not web-accessible.
|
Use a hidden per-site security folder so `uploadshield.php` is not web-accessible.
|
||||||
|
|
||||||
Example layout:
|
Example layout:
|
||||||
|
|
||||||
@@ -41,16 +41,16 @@ Example layout:
|
|||||||
├── app/
|
├── app/
|
||||||
├── uploads/
|
├── uploads/
|
||||||
├── .security/
|
├── .security/
|
||||||
│ ├── upload-logger.php
|
│ ├── uploadshield.php
|
||||||
│ └── logs/
|
│ └── logs/
|
||||||
│ └── uploads.log
|
│ └── uploads.log
|
||||||
├── quarantine/
|
├── quarantine/
|
||||||
└── state/
|
└── state/
|
||||||
```
|
Place `uploadshield.php` into `.security/uploadshield.php`.
|
||||||
|
|
||||||
**3. Copy files & configure**
|
**3. Copy files & configure**
|
||||||
- Place `upload-logger.php` into `.security/upload-logger.php`.
|
- Place `uploadshield.php` into `.security/uploadshield.php`.
|
||||||
- Copy `upload-logger.json` from the repository to the same directory and edit paths to absolute values, e.g.:
|
- Copy `uploadshield.json` from the repository to the same directory and edit paths to absolute values, e.g.:
|
||||||
- `paths.log_file` → `/var/www/sites/example-site/.security/logs/uploads.log`
|
- `paths.log_file` → `/var/www/sites/example-site/.security/logs/uploads.log`
|
||||||
- `paths.quarantine_dir` → `/var/www/sites/example-site/quarantine`
|
- `paths.quarantine_dir` → `/var/www/sites/example-site/quarantine`
|
||||||
- `paths.state_dir` → `/var/www/sites/example-site/state`
|
- `paths.state_dir` → `/var/www/sites/example-site/state`
|
||||||
@@ -81,14 +81,14 @@ chmod 0640 /var/www/sites/example-site/.security/logs/uploads.log
|
|||||||
|
|
||||||
Alternatively use the included provisioning scripts on the host:
|
Alternatively use the included provisioning scripts on the host:
|
||||||
- `scripts/provision_dirs.sh` — run as root; idempotent and attempts to set SELinux fcontext.
|
- `scripts/provision_dirs.sh` — run as root; idempotent and attempts to set SELinux fcontext.
|
||||||
- `scripts/ansible/upload-logger-provision.yml` — Ansible playbook for bulk provisioning.
|
- `scripts/ansible/uploadshield-provision.yml` — Ansible playbook for bulk provisioning.
|
||||||
|
|
||||||
**5. PHP‑FPM configuration (per-site pool)**
|
**5. PHP‑FPM configuration (per-site pool)**
|
||||||
Edit the site's FPM pool (example: `/etc/php/8.1/fpm/pool.d/example-site.conf`) and add:
|
Edit the site's FPM pool (example: `/etc/php/8.1/fpm/pool.d/example-site.conf`) and add:
|
||||||
|
php_admin_value[auto_prepend_file] = /var/www/sites/example-site/.security/uploadshield.php
|
||||||
```ini
|
```ini
|
||||||
; Ensure upload-logger runs before application code
|
; Ensure uploadshield runs before application code
|
||||||
php_admin_value[auto_prepend_file] = /var/www/sites/example-site/.security/upload-logger.php
|
php_admin_value[auto_prepend_file] = /var/www/sites/example-site/.security/uploadshield.php
|
||||||
```
|
```
|
||||||
|
|
||||||
Then reload PHP-FPM:
|
Then reload PHP-FPM:
|
||||||
@@ -98,11 +98,11 @@ sudo systemctl reload php8.1-fpm
|
|||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- Use the absolute path to `upload-logger.php`.
|
- Use the absolute path to `uploadshield.php`.
|
||||||
- If your host uses a shared PHP-FPM pool, consider enabling per-vhost `auto_prepend_file` via nginx/apache or create a dedicated pool for the site.
|
- If your host uses a shared PHP-FPM pool, consider enabling per-vhost `auto_prepend_file` via nginx/apache or create a dedicated pool for the site.
|
||||||
|
|
||||||
**6. Log rotation**
|
**6. Log rotation**
|
||||||
Create `/etc/logrotate.d/upload-logger` with content adapted to your paths:
|
Create `/etc/logrotate.d/uploadshield` with content adapted to your paths:
|
||||||
|
|
||||||
```
|
```
|
||||||
/var/www/sites/example-site/.security/logs/uploads.log {
|
/var/www/sites/example-site/.security/logs/uploads.log {
|
||||||
@@ -129,10 +129,10 @@ Create `/etc/logrotate.d/upload-logger` with content adapted to your paths:
|
|||||||
- Upload a file containing `<?php` to confirm detectors log `suspicious_upload` entries.
|
- Upload a file containing `<?php` to confirm detectors log `suspicious_upload` entries.
|
||||||
- Check `logs/uploads.log` for events.
|
- Check `logs/uploads.log` for events.
|
||||||
|
|
||||||
Example quick test (on host):
|
php -S 127.0.0.1:8000 -t . -d auto_prepend_file=/var/www/sites/example-site/.security/uploadshield.php
|
||||||
```bash
|
```bash
|
||||||
# from site root
|
# from site root
|
||||||
php -S 127.0.0.1:8000 -t . -d auto_prepend_file=/var/www/sites/example-site/.security/upload-logger.php
|
php -S 127.0.0.1:8000 -t . -d auto_prepend_file=/var/www/sites/example-site/.security/uploadshield.php
|
||||||
# then curl a file POST to your test endpoint
|
# then curl a file POST to your test endpoint
|
||||||
curl -F "file=@/path/to/sample.txt" http://127.0.0.1:8000/upload_test.php
|
curl -F "file=@/path/to/sample.txt" http://127.0.0.1:8000/upload_test.php
|
||||||
```
|
```
|
||||||
@@ -146,7 +146,7 @@ curl -F "file=@/path/to/sample.txt" http://127.0.0.1:8000/upload_test.php
|
|||||||
|
|
||||||
**9. Gradual enable blocking**
|
**9. Gradual enable blocking**
|
||||||
- After tuning, enable blocking in a controlled manner:
|
- After tuning, enable blocking in a controlled manner:
|
||||||
1. Set `ops.block_suspicious` → `true` in `upload-logger.json` on a small subset of sites or a canary host.
|
1. Set `ops.block_suspicious` → `true` in `uploadshield.json` on a small subset of sites or a canary host.
|
||||||
2. Monitor errors, rollback quickly if issues appear.
|
2. Monitor errors, rollback quickly if issues appear.
|
||||||
3. Gradually roll out to remaining hosts.
|
3. Gradually roll out to remaining hosts.
|
||||||
|
|
||||||
@@ -159,17 +159,17 @@ curl -F "file=@/path/to/sample.txt" http://127.0.0.1:8000/upload_test.php
|
|||||||
- Ensure `quarantine/` and `.security/logs` are not accessible from the web server.
|
- Ensure `quarantine/` and `.security/logs` are not accessible from the web server.
|
||||||
- Verify SELinux/AppArmor contexts after running provisioning.
|
- Verify SELinux/AppArmor contexts after running provisioning.
|
||||||
- Ensure owner/group are set to root and web group (e.g., `root:www-data`) and modes match the guide.
|
- Ensure owner/group are set to root and web group (e.g., `root:www-data`) and modes match the guide.
|
||||||
- Keep `upload-logger.php` readable by root (644) and the logs readable only by the intended group (640).
|
Keep `uploadshield.php` readable by root (644) and the logs readable only by the intended group (640).
|
||||||
|
|
||||||
**Rollback**
|
**Rollback**
|
||||||
- Disable `php_admin_value[auto_prepend_file]` in the pool and reload PHP-FPM.
|
- Disable `php_admin_value[auto_prepend_file]` in the pool and reload PHP-FPM.
|
||||||
- Remove or rotate the `upload-logger` files if needed.
|
- Remove or rotate the `uploadshield` files if needed.
|
||||||
|
|
||||||
**Further reading & files**
|
**Further reading & files**
|
||||||
- Integration notes: [INTEGRATION.md](INTEGRATION.md)
|
- Integration notes: [INTEGRATION.md](INTEGRATION.md)
|
||||||
- Provisioning script: `scripts/provision_dirs.sh`
|
- Provisioning script: `scripts/provision_dirs.sh`
|
||||||
- Ansible playbook: `scripts/ansible/upload-logger-provision.yml`
|
- Ansible playbook: `scripts/ansible/uploadshield-provision.yml`
|
||||||
- Example configuration: `upload-logger.json`
|
- Example configuration: `uploadshield.json`
|
||||||
|
|
||||||
---
|
---
|
||||||
If you want, I can: (a) generate a site-specific copy of these snippets for your exact paths/PHP version, (b) open a PR with the updated documentation, or (c) produce a one-command installer playbook that runs the provisioning and copies files to a remote host. Tell me which option you prefer.
|
If you want, I can: (a) generate a site-specific copy of these snippets for your exact paths/PHP version, (b) open a PR with the updated documentation, or (c) produce a one-command installer playbook that runs the provisioning and copies files to a remote host. Tell me which option you prefer.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Release & Deploy Checklist
|
# Release & Deploy Checklist
|
||||||
|
|
||||||
This checklist helps you deploy UploadShield's primary script (`upload-logger.php`) to production safely.
|
This checklist helps you deploy UploadShield's primary script (`uploadshield.php`) to production safely.
|
||||||
|
|
||||||
## Pre-release
|
## Pre-release
|
||||||
|
|
||||||
- [ ] Review and pin configuration in `upload-logger.json` (see `examples/upload-logger.json`).
|
- [ ] Review and pin configuration in `uploadshield.json` (see `examples/uploadshield.json`).
|
||||||
- [ ] Ensure unit tests pass and CI workflows are green for the release branch.
|
- [ ] Ensure unit tests pass and CI workflows are green for the release branch.
|
||||||
- [ ] Run static analysis (`vendor/bin/phpstan analyse`) and fix any new issues.
|
- [ ] Run static analysis (`vendor/bin/phpstan analyse`) and fix any new issues.
|
||||||
- [ ] Run `composer audit` to confirm no advisories remain.
|
- [ ] Run `composer audit` to confirm no advisories remain.
|
||||||
@@ -28,7 +28,7 @@ This checklist helps you deploy UploadShield's primary script (`upload-logger.ph
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
- [ ] Create `upload-logger.json` from `examples/upload-logger.json` and adjust values:
|
- [ ] Create `uploadshield.json` from `examples/uploadshield.json` and adjust values:
|
||||||
- `paths.quarantine_dir` — absolute path to `quarantine/`.
|
- `paths.quarantine_dir` — absolute path to `quarantine/`.
|
||||||
- `paths.state_dir` — absolute path to `state/`.
|
- `paths.state_dir` — absolute path to `state/`.
|
||||||
- `paths.allowlist_file` — path to `allowlist.json`.
|
- `paths.allowlist_file` — path to `allowlist.json`.
|
||||||
@@ -37,7 +37,7 @@ This checklist helps you deploy UploadShield's primary script (`upload-logger.ph
|
|||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
- [ ] Ensure `php_admin_value[auto_prepend_file]` is configured in the site pool for PHP-FPM to include `upload-logger.php` (UploadShield).
|
- [ ] Ensure `php_admin_value[auto_prepend_file]` is configured in the site pool for PHP-FPM to include `uploadshield.php` (UploadShield).
|
||||||
- [ ] Reload or restart PHP-FPM gracefully after changing pool settings.
|
- [ ] Reload or restart PHP-FPM gracefully after changing pool settings.
|
||||||
- [ ] Verify the web server denies direct access to `logs/` and `quarantine/`.
|
- [ ] Verify the web server denies direct access to `logs/` and `quarantine/`.
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ This checklist helps you deploy UploadShield's primary script (`upload-logger.ph
|
|||||||
|
|
||||||
## Post-release
|
## Post-release
|
||||||
|
|
||||||
- [ ] Configure log rotation (see `examples/logrotate.d/upload-logger`).
|
- [ ] Configure log rotation (see `examples/logrotate.d/uploadshield`).
|
||||||
- [ ] Set up monitoring/alerting on log file growth, error events, and flood alerts.
|
- [ ] Set up monitoring/alerting on log file growth, error events, and flood alerts.
|
||||||
- [ ] Schedule periodic dependency checks (Dependabot and weekly `composer audit`).
|
- [ ] Schedule periodic dependency checks (Dependabot and weekly `composer audit`).
|
||||||
- [ ] Periodically review `allowlist.json` and detector tuning to reduce false positives.
|
- [ ] Periodically review `allowlist.json` and detector tuning to reduce false positives.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<phpunit bootstrap="vendor/autoload.php" colors="true">
|
<phpunit bootstrap="vendor/autoload.php" colors="true">
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="upload-logger">
|
<testsuite name="uploadshield">
|
||||||
<directory>tests</directory>
|
<directory>tests</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
- hosts: web
|
- hosts: web
|
||||||
become: true
|
become: true
|
||||||
vars:
|
vars:
|
||||||
upload_logger_root: "{{ playbook_dir | default('.') | dirname | realpath }}"
|
uploadshield_root: "{{ playbook_dir | default('.') | dirname | realpath }}"
|
||||||
logs_dir: "{{ upload_logger_root }}/logs"
|
logs_dir: "{{ uploadshield_root }}/logs"
|
||||||
quarantine_dir: "{{ upload_logger_root }}/quarantine"
|
quarantine_dir: "{{ uploadshield_root }}/quarantine"
|
||||||
state_dir: "{{ upload_logger_root }}/state"
|
state_dir: "{{ uploadshield_root }}/state"
|
||||||
examples_dir: "{{ upload_logger_root }}/examples"
|
examples_dir: "{{ uploadshield_root }}/examples"
|
||||||
quarantine_owner: "root"
|
quarantine_owner: "root"
|
||||||
quarantine_group: "www-data"
|
quarantine_group: "www-data"
|
||||||
quarantine_perms: "0700"
|
quarantine_perms: "0700"
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
logs_perms: "0750"
|
logs_perms: "0750"
|
||||||
log_file_mode: "0640"
|
log_file_mode: "0640"
|
||||||
selinux_fcontext: "httpd_sys_rw_content_t"
|
selinux_fcontext: "httpd_sys_rw_content_t"
|
||||||
tmpfiles_conf: "/etc/tmpfiles.d/upload-logger.conf"
|
tmpfiles_conf: "/etc/tmpfiles.d/uploadshield.conf"
|
||||||
logrotate_dest: "/etc/logrotate.d/upload-logger"
|
logrotate_dest: "/etc/logrotate.d/uploadshield"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Ensure logs directory exists
|
- name: Ensure logs directory exists
|
||||||
@@ -45,14 +45,14 @@
|
|||||||
group: "{{ quarantine_group }}"
|
group: "{{ quarantine_group }}"
|
||||||
mode: "{{ state_perms }}"
|
mode: "{{ state_perms }}"
|
||||||
|
|
||||||
- name: Ensure example upload-logger.json is copied (only when missing)
|
- name: Ensure example uploadshield.json is copied (only when missing)
|
||||||
copy:
|
copy:
|
||||||
src: "{{ examples_dir }}/upload-logger.json"
|
src: "{{ examples_dir }}/uploadshield.json"
|
||||||
dest: "{{ upload_logger_root }}/upload-logger.json"
|
dest: "{{ uploadshield_root }}/uploadshield.json"
|
||||||
owner: "{{ quarantine_owner }}"
|
owner: "{{ quarantine_owner }}"
|
||||||
group: "{{ quarantine_group }}"
|
group: "{{ quarantine_group }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
when: not (upload_logger_root + '/upload-logger.json') | path_exists
|
when: not (uploadshield_root + '/uploadshield.json') | path_exists
|
||||||
|
|
||||||
- name: Install tmpfiles.d entry to recreate dirs at boot
|
- name: Install tmpfiles.d entry to recreate dirs at boot
|
||||||
copy:
|
copy:
|
||||||
@@ -66,12 +66,12 @@
|
|||||||
|
|
||||||
- name: Install logrotate snippet if example exists
|
- name: Install logrotate snippet if example exists
|
||||||
copy:
|
copy:
|
||||||
src: "{{ examples_dir }}/logrotate.d/upload-logger"
|
src: "{{ examples_dir }}/logrotate.d/uploadshield"
|
||||||
dest: "{{ logrotate_dest }}"
|
dest: "{{ logrotate_dest }}"
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: '0644'
|
mode: '0644'
|
||||||
when: (examples_dir + '/logrotate.d/upload-logger') | path_exists
|
when: (examples_dir + '/logrotate.d/uploadshield') | path_exists
|
||||||
|
|
||||||
- name: Set SELinux fcontext for directories when selinux enabled
|
- name: Set SELinux fcontext for directories when selinux enabled
|
||||||
when: ansible_selinux.status == 'enabled'
|
when: ansible_selinux.status == 'enabled'
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
---
|
---
|
||||||
# Ansible playbook snippet to provision UploadShield directories and permissions.
|
# Ansible playbook snippet to provision UploadShield directories and permissions.
|
||||||
# Usage: ansible-playbook -i inventory scripts/ansible/upload-logger-provision.yml
|
# Usage: ansible-playbook -i inventory scripts/ansible/uploadshield-provision.yml
|
||||||
|
|
||||||
- hosts: web
|
- hosts: web
|
||||||
become: true
|
become: true
|
||||||
vars:
|
vars:
|
||||||
upload_logger_root: "{{ playbook_dir | default('.') | dirname | realpath }}"
|
uploadshield_root: "{{ playbook_dir | default('.') | dirname | realpath }}"
|
||||||
quarantine_dir: "{{ upload_logger_root }}/quarantine"
|
quarantine_dir: "{{ uploadshield_root }}/quarantine"
|
||||||
state_dir: "{{ upload_logger_root }}/state"
|
state_dir: "{{ uploadshield_root }}/state"
|
||||||
quarantine_owner: "root"
|
quarantine_owner: "root"
|
||||||
quarantine_group: "www-data"
|
quarantine_group: "www-data"
|
||||||
quarantine_perms: "0700"
|
quarantine_perms: "0700"
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
# Provision quarantine and state directories for UploadShield (upload-logger.php)
|
# Provision quarantine and state directories for UploadShield (uploadshield.php)
|
||||||
# Usage: sudo ./provision_dirs.sh [--config path/to/upload-logger.json]
|
# Usage: sudo ./provision_dirs.sh [--config path/to/uploadshield.json]
|
||||||
|
|
||||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
CFG="${1:-$ROOT_DIR/upload-logger.json}"
|
CFG="${1:-$ROOT_DIR/uploadshield.json}"
|
||||||
|
|
||||||
QUIET=0
|
QUIET=0
|
||||||
if [[ "${2:-}" == "--quiet" ]]; then QUIET=1; fi
|
if [[ "${2:-}" == "--quiet" ]]; then QUIET=1; fi
|
||||||
@@ -85,7 +85,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Optional tmpfiles.d entry to recreate directories at boot (idempotent)
|
# Optional tmpfiles.d entry to recreate directories at boot (idempotent)
|
||||||
TMPFILE="/etc/tmpfiles.d/upload-logger.conf"
|
TMPFILE="/etc/tmpfiles.d/uploadshield.conf"
|
||||||
if [[ -w /etc/tmpfiles.d || $QUIET -eq 1 ]]; then
|
if [[ -w /etc/tmpfiles.d || $QUIET -eq 1 ]]; then
|
||||||
info "Writing tmpfiles.d entry to ${TMPFILE}"
|
info "Writing tmpfiles.d entry to ${TMPFILE}"
|
||||||
cat > "$TMPFILE" <<EOF
|
cat > "$TMPFILE" <<EOF
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
ACTIVE_CFG="$ROOT_DIR/upload-logger.json"
|
ACTIVE_CFG="$ROOT_DIR/uploadshield.json"
|
||||||
PROD_CFG="$ROOT_DIR/config/upload-logger.prod.json"
|
PROD_CFG="$ROOT_DIR/config/uploadshield.prod.json"
|
||||||
BLOCK_CFG="$ROOT_DIR/config/upload-logger.blocking.json"
|
BLOCK_CFG="$ROOT_DIR/config/uploadshield.blocking.json"
|
||||||
BACKUP_DIR="$ROOT_DIR/config/backups"
|
BACKUP_DIR="$ROOT_DIR/config/backups"
|
||||||
DRY_RUN=0
|
DRY_RUN=0
|
||||||
CONFIRM=0
|
CONFIRM=0
|
||||||
@@ -46,8 +46,8 @@ fi
|
|||||||
mkdir -p "$BACKUP_DIR"
|
mkdir -p "$BACKUP_DIR"
|
||||||
TS=$(date +%Y%m%dT%H%M%S)
|
TS=$(date +%Y%m%dT%H%M%S)
|
||||||
if [[ -f "$ACTIVE_CFG" ]]; then
|
if [[ -f "$ACTIVE_CFG" ]]; then
|
||||||
cp -a "$ACTIVE_CFG" "$BACKUP_DIR/upload-logger.json.bak.$TS"
|
cp -a "$ACTIVE_CFG" "$BACKUP_DIR/uploadshield.json.bak.$TS"
|
||||||
echo "Backed up current config to $BACKUP_DIR/upload-logger.json.bak.$TS"
|
echo "Backed up current config to $BACKUP_DIR/uploadshield.json.bak.$TS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp -a "$BLOCK_CFG" "$ACTIVE_CFG"
|
cp -a "$BLOCK_CFG" "$ACTIVE_CFG"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ After=network.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
ExecStart=/usr/local/bin/upload-logger-provision.sh /opt/upload-logger/upload-logger.json
|
ExecStart=/usr/local/bin/uploadshield-provision.sh /opt/uploadshield/uploadshield.json
|
||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Simple endpoint that accepts file uploads; auto_prepend_file will run upload-logger.php
|
// Simple endpoint that accepts file uploads; auto_prepend_file will run uploadshield.php
|
||||||
header('Content-Type: text/plain');
|
header('Content-Type: text/plain');
|
||||||
if (empty($_FILES)) {
|
if (empty($_FILES)) {
|
||||||
echo "no files\n";
|
echo "no files\n";
|
||||||
|
|||||||
@@ -1,20 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Global Upload Logger (Hardened v3) — part of UploadShield
|
* UploadShield runtime
|
||||||
* Project: TheWallpapers
|
|
||||||
*
|
|
||||||
* Purpose:
|
* Purpose:
|
||||||
* - Log ALL normal uploads via $_FILES (single + multi)
|
* - Log uploads and detect suspicious uploads before application code runs
|
||||||
* - Detect common evasion (double extensions, fake images, path tricks, PHP payload in non-php files)
|
* - Configured via `uploadshield.json` (env `UPLOADSHIELD_CONFIG` supported)
|
||||||
* - Log suspicious "raw body" uploads (php://input / octet-stream) that bypass $_FILES
|
|
||||||
* - Optional blocking mode
|
|
||||||
*
|
*
|
||||||
* Install:
|
* Install as PHP-FPM pool `auto_prepend_file=/path/to/uploadshield.php`
|
||||||
* - Use as PHP-FPM pool `auto_prepend_file=.../upload_logger.php`
|
|
||||||
*
|
|
||||||
* Notes:
|
|
||||||
* - This cannot *guarantee* interception of every file-write exploit (file_put_contents, ZipArchive extract, etc.)
|
|
||||||
* but it catches most real-world upload vectors and provides strong forensic logging.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Ignore CLI
|
// Ignore CLI
|
||||||
@@ -74,11 +65,11 @@ $PEEK_RAW_INPUT = false;
|
|||||||
$TRUSTED_PROXY_IPS = ['127.0.0.1', '::1'];
|
$TRUSTED_PROXY_IPS = ['127.0.0.1', '::1'];
|
||||||
|
|
||||||
// Environment variable name or marker file to explicitly allow peeking
|
// Environment variable name or marker file to explicitly allow peeking
|
||||||
$ALLOW_PEEK_ENV = 'UPLOAD_LOGGER_ALLOW_PEEK';
|
$ALLOW_PEEK_ENV = 'UPLOADSHIELD_ALLOW_PEEK';
|
||||||
$PEEK_ALLOW_FILE = __DIR__ . '/.upload_logger_allow_peek';
|
$PEEK_ALLOW_FILE = __DIR__ . '/.uploadshield_allow_peek';
|
||||||
|
|
||||||
// Auto-enable peek only when explicitly allowed by environment/file or when a
|
// Auto-enable peek only when explicitly allowed by environment/file or when a
|
||||||
// trusted frontend indicates the body was buffered via header `X-Upload-Logger-Peek: 1`.
|
// trusted frontend indicates the body was buffered via header `X-UploadShield-Peek: 1`.
|
||||||
// This avoids consuming request bodies unexpectedly.
|
// This avoids consuming request bodies unexpectedly.
|
||||||
try {
|
try {
|
||||||
$envAllow = getenv($ALLOW_PEEK_ENV) === '1';
|
$envAllow = getenv($ALLOW_PEEK_ENV) === '1';
|
||||||
@@ -115,7 +106,7 @@ $BASE64_ALLOWLIST_CTYPE = [];
|
|||||||
|
|
||||||
// Allowlist file location and environment override
|
// Allowlist file location and environment override
|
||||||
$ALLOWLIST_FILE_DEFAULT = __DIR__ . '/allowlist.json';
|
$ALLOWLIST_FILE_DEFAULT = __DIR__ . '/allowlist.json';
|
||||||
$ALLOWLIST_FILE = getenv('UPLOAD_LOGGER_ALLOWLIST') ?: $ALLOWLIST_FILE_DEFAULT;
|
$ALLOWLIST_FILE = getenv('UPLOADSHIELD_ALLOWLIST') ?: $ALLOWLIST_FILE_DEFAULT;
|
||||||
|
|
||||||
if (is_file($ALLOWLIST_FILE)) {
|
if (is_file($ALLOWLIST_FILE)) {
|
||||||
$raw = @file_get_contents($ALLOWLIST_FILE);
|
$raw = @file_get_contents($ALLOWLIST_FILE);
|
||||||
@@ -131,9 +122,9 @@ if (is_file($ALLOWLIST_FILE)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load config (JSON) or fall back to inline defaults.
|
// Load config (JSON) or fall back to inline defaults.
|
||||||
// Config file path may be overridden with env `UPLOAD_LOGGER_CONFIG`.
|
// Config file path may be overridden with env `UPLOADSHIELD_CONFIG`.
|
||||||
$CONFIG_FILE_DEFAULT = __DIR__ . '/upload-logger.json';
|
$CONFIG_FILE_DEFAULT = __DIR__ . '/uploadshield.json';
|
||||||
$CONFIG_FILE = getenv('UPLOAD_LOGGER_CONFIG') ?: $CONFIG_FILE_DEFAULT;
|
$CONFIG_FILE = getenv('UPLOADSHIELD_CONFIG') ?: $CONFIG_FILE_DEFAULT;
|
||||||
|
|
||||||
// Default modules and settings
|
// Default modules and settings
|
||||||
$DEFAULT_CONFIG = [
|
$DEFAULT_CONFIG = [
|
||||||
@@ -168,7 +159,7 @@ if (is_file($CONFIG_FILE)) {
|
|||||||
|
|
||||||
$cfgLogFile = $CONFIG_DATA['paths']['log_file'] ?? null;
|
$cfgLogFile = $CONFIG_DATA['paths']['log_file'] ?? null;
|
||||||
if (is_string($cfgLogFile) && $cfgLogFile !== '') {
|
if (is_string($cfgLogFile) && $cfgLogFile !== '') {
|
||||||
$isAbs = preg_match('#^[A-Za-z]:[\\/]#', $cfgLogFile) === 1
|
$isAbs = preg_match('#^[A-Za-z]:[\/]#', $cfgLogFile) === 1
|
||||||
|| (strlen($cfgLogFile) > 0 && ($cfgLogFile[0] === '/' || $cfgLogFile[0] === '\\'));
|
|| (strlen($cfgLogFile) > 0 && ($cfgLogFile[0] === '/' || $cfgLogFile[0] === '\\'));
|
||||||
if ($isAbs) {
|
if ($isAbs) {
|
||||||
$logFile = $cfgLogFile;
|
$logFile = $cfgLogFile;
|
||||||
@@ -183,7 +174,7 @@ $BOOT_LOGGER = new \UploadLogger\Core\Services\LogService($logFile, []);
|
|||||||
|
|
||||||
$fileAllow = is_file($PEEK_ALLOW_FILE);
|
$fileAllow = is_file($PEEK_ALLOW_FILE);
|
||||||
$headerAllow = false;
|
$headerAllow = false;
|
||||||
if (isset($_SERVER['HTTP_X_UPLOAD_LOGGER_PEEK']) && $_SERVER['HTTP_X_UPLOAD_LOGGER_PEEK'] === '1') {
|
if (isset($_SERVER['HTTP_X_UPLOADSHIELD_PEEK']) && $_SERVER['HTTP_X_UPLOADSHIELD_PEEK'] === '1') {
|
||||||
$clientIp = $REQ->getClientIp();
|
$clientIp = $REQ->getClientIp();
|
||||||
if (in_array($clientIp, $TRUSTED_PROXY_IPS, true)) {
|
if (in_array($clientIp, $TRUSTED_PROXY_IPS, true)) {
|
||||||
$headerAllow = true;
|
$headerAllow = true;
|
||||||
@@ -380,7 +371,7 @@ $LOGGER = new \UploadLogger\Core\Logger($logFile, $REQUEST_CTX, $CONFIG);
|
|||||||
/*
|
/*
|
||||||
* Map frequently-used legacy globals to values from `Config` so the rest of
|
* Map frequently-used legacy globals to values from `Config` so the rest of
|
||||||
* the procedural helpers can continue to reference globals but operators
|
* the procedural helpers can continue to reference globals but operators
|
||||||
* may control behavior via `upload-logger.json`.
|
* may control behavior via `uploadshield.json`.
|
||||||
*/
|
*/
|
||||||
$BLOCK_SUSPICIOUS = $CONFIG->get('ops.block_suspicious', $BLOCK_SUSPICIOUS ?? false);
|
$BLOCK_SUSPICIOUS = $CONFIG->get('ops.block_suspicious', $BLOCK_SUSPICIOUS ?? false);
|
||||||
$MAX_SIZE = (int)$CONFIG->get('limits.max_size', $MAX_SIZE ?? (50 * 1024 * 1024));
|
$MAX_SIZE = (int)$CONFIG->get('limits.max_size', $MAX_SIZE ?? (50 * 1024 * 1024));
|
||||||
Reference in New Issue
Block a user