From cd88abd598568ebd6b3bef83c52f5969e7e2fbed Mon Sep 17 00:00:00 2001 From: Hammed Oyedele Date: Fri, 14 Aug 2026 23:35:07 +0100 Subject: [PATCH 1/2] fix: update security rules to prevent edge cases in PHP execution for storage directories Expanded the regex patterns in Apache, Nginx, and Caddy configurations to block PHP file execution more reliably by accounting for trailing slashes after `.php`. --- .../fpm-apache/etc/apache2/conf-available/security.conf | 4 ++-- .../fpm-nginx/etc/nginx/site-opts.d/http.conf.template | 8 ++++---- .../fpm-nginx/etc/nginx/site-opts.d/https.conf.template | 8 ++++---- src/variations/frankenphp/etc/frankenphp/Caddyfile | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/variations/fpm-apache/etc/apache2/conf-available/security.conf b/src/variations/fpm-apache/etc/apache2/conf-available/security.conf index f572f2501..945308494 100644 --- a/src/variations/fpm-apache/etc/apache2/conf-available/security.conf +++ b/src/variations/fpm-apache/etc/apache2/conf-available/security.conf @@ -57,7 +57,7 @@ Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains # Block PHP execution in storage directory to prevent uploaded malicious PHP files from running # Reference: Livewire arbitrary file upload (GHSA-29cq-5w36-x7w3) - + Require all denied @@ -80,4 +80,4 @@ Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains Require all denied # allow from xxx.xxx.xxx.xxx - \ No newline at end of file + diff --git a/src/variations/fpm-nginx/etc/nginx/site-opts.d/http.conf.template b/src/variations/fpm-nginx/etc/nginx/site-opts.d/http.conf.template index 08a90ff96..319843589 100644 --- a/src/variations/fpm-nginx/etc/nginx/site-opts.d/http.conf.template +++ b/src/variations/fpm-nginx/etc/nginx/site-opts.d/http.conf.template @@ -15,7 +15,7 @@ absolute_redirect off; # Healthcheck: Set /healthcheck to be the static health check URL location /healthcheck { access_log off; - + # set max 5 seconds for healthcheck fastcgi_read_timeout 5s; @@ -32,7 +32,7 @@ location / { # Block PHP execution in storage directory to prevent uploaded malicious PHP files from running # Reference: Livewire arbitrary file upload (GHSA-29cq-5w36-x7w3) -location ~* ^/storage/.*\.php$ { +location ~* ^/storage/.*\.php(?:/|$) { deny all; } @@ -42,10 +42,10 @@ location ~ \.php$ { fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; - fastcgi_buffers $NGINX_FASTCGI_BUFFERS; + fastcgi_buffers $NGINX_FASTCGI_BUFFERS; fastcgi_buffer_size $NGINX_FASTCGI_BUFFER_SIZE; fastcgi_read_timeout $PHP_MAX_EXECUTION_TIME; } # additional config -include /etc/nginx/server-opts.d/*.conf; \ No newline at end of file +include /etc/nginx/server-opts.d/*.conf; diff --git a/src/variations/fpm-nginx/etc/nginx/site-opts.d/https.conf.template b/src/variations/fpm-nginx/etc/nginx/site-opts.d/https.conf.template index 810ff0747..fce7efa3c 100644 --- a/src/variations/fpm-nginx/etc/nginx/site-opts.d/https.conf.template +++ b/src/variations/fpm-nginx/etc/nginx/site-opts.d/https.conf.template @@ -21,7 +21,7 @@ absolute_redirect off; # Healthcheck: Set /healthcheck to be the static health check URL location /healthcheck { access_log off; - + # set max 5 seconds for healthcheck fastcgi_read_timeout 5s; @@ -38,7 +38,7 @@ location / { # Block PHP execution in storage directory to prevent uploaded malicious PHP files from running # Reference: Livewire arbitrary file upload (GHSA-29cq-5w36-x7w3) -location ~* ^/storage/.*\.php$ { +location ~* ^/storage/.*\.php(?:/|$) { deny all; } @@ -48,10 +48,10 @@ location ~ \.php$ { fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; - fastcgi_buffers $NGINX_FASTCGI_BUFFERS; + fastcgi_buffers $NGINX_FASTCGI_BUFFERS; fastcgi_buffer_size $NGINX_FASTCGI_BUFFER_SIZE; fastcgi_read_timeout $PHP_MAX_EXECUTION_TIME; } # additional config -include /etc/nginx/server-opts.d/*.conf; \ No newline at end of file +include /etc/nginx/server-opts.d/*.conf; diff --git a/src/variations/frankenphp/etc/frankenphp/Caddyfile b/src/variations/frankenphp/etc/frankenphp/Caddyfile index 50b2158be..add4cd7c4 100644 --- a/src/variations/frankenphp/etc/frankenphp/Caddyfile +++ b/src/variations/frankenphp/etc/frankenphp/Caddyfile @@ -97,7 +97,7 @@ fd00::/8 \ file_server import performance - import security + import security {$CADDY_SERVER_EXTRA_DIRECTIVES} } @@ -140,7 +140,7 @@ fd00::/8 \ # Block PHP execution in storage directory to prevent uploaded malicious PHP files from running # Reference: Livewire arbitrary file upload (GHSA-29cq-5w36-x7w3) - @storage-php path_regexp ^/storage/.*\.php$ + @storage-php path_regexp ^/storage/.*\.php(?:/|$) respond @storage-php 403 # Block access to files that may expose sensitive information From 891b8623420f19e8f84b0c1510b8525e0f433613 Mon Sep 17 00:00:00 2001 From: Jay Rogers Date: Thu, 10 Sep 2026 15:29:20 +0000 Subject: [PATCH 2/2] Add storage directory and enforce PHP execution restrictions --- scripts/test-image.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/test-image.sh b/scripts/test-image.sh index 25e92045f..dc4c7163f 100755 --- a/scripts/test-image.sh +++ b/scripts/test-image.sh @@ -72,6 +72,10 @@ if [ -n "$http_port" ]; then chmod 755 "$web_dir" echo ' "$web_dir/index.php" chmod 644 "$web_dir/index.php" + mkdir -p "$web_dir/storage" + chmod 755 "$web_dir/storage" + echo ' "$web_dir/storage/uploaded.php" + chmod 644 "$web_dir/storage/uploaded.php" run_args+=(--publish "127.0.0.1::${http_port}" --volume "$web_dir:$web_root:ro") fi @@ -120,3 +124,11 @@ if [ "$body" != "serversideup-php-ok:${php_version}" ]; then fail "Web server did not serve index.php on port ${http_port}. Response: ${body:-}" fi pass "Web server serves PHP on port ${http_port}" + +# Uploaded PHP files under /storage must never run, including through PATH_INFO +# (/storage/file.php/anything), which Apache and FrankenPHP would otherwise execute. +for path in /storage/uploaded.php /storage/uploaded.php/anything; do + response=$(curl --silent --max-time 5 --output /dev/null --write-out '%{http_code}' "http://127.0.0.1:${host_port}${path}" || true) + [ "$response" = "403" ] || fail "Expected ${path} to return 403, got ${response:-}" +done +pass "Web server blocks PHP execution under /storage"