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" 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 217b697b7..cab741f5c 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 02d9c54fb..196bf7bfb 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; } @@ -47,7 +47,7 @@ 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; } @@ -56,4 +56,4 @@ location ~ \.php$ { include /etc/nginx/trusted-proxy/${TRUSTED_PROXY}.conf; # 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 547bfe1b3..a1daf51ba 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; } @@ -53,7 +53,7 @@ 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; } @@ -62,4 +62,4 @@ location ~ \.php$ { include /etc/nginx/trusted-proxy/${TRUSTED_PROXY}.conf; # 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 704cee3dd..a32089016 100644 --- a/src/variations/frankenphp/etc/frankenphp/Caddyfile +++ b/src/variations/frankenphp/etc/frankenphp/Caddyfile @@ -72,7 +72,7 @@ file_server import performance - import security + import security {$CADDY_SERVER_EXTRA_DIRECTIVES} } @@ -115,7 +115,7 @@ # 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