Keep uploads outside the web root.

save_uploaded_file() read, and prune_images() deleted, any path in the
"uploaded" POST field. Uploads now go to UPLOAD_DIR under a random name
that only the session knows.
This commit is contained in:
tim
2026-09-25 21:31:47 +02:00
parent c85f54116c
commit e881ddb458
7 changed files with 104 additions and 41 deletions
+48
View File
@@ -350,3 +350,51 @@ function which_table($move = FALSE, $action = 'read') {
return 'xxx';
}
}
/*
* Uploaded images wait in a directory outside the web root until the
* story is saved. The session holds the random file name, so the client
* never sends a file path.
*/
function upload_dir() {
$dir = rtrim(config('UPLOAD_DIR') ?: sys_get_temp_dir() . '/xxx-uploads', '/');
if (!is_dir($dir))
mkdir($dir, 0700, TRUE);
return $dir;
}
function is_upload_name($name) {
return is_string($name) && preg_match('/^[0-9a-f]{32}$/', $name);
}
/*
* Move a file from $_FILES into the upload directory, and remember it
* in the session. Returns the new path, or FALSE.
*/
function store_upload($tmp_file) {
$name = bin2hex(random_bytes(16));
$file = upload_dir() . '/' . $name;
if (!move_uploaded_file($tmp_file, $file))
return FALSE;
$_SESSION['pending_upload'] = $name;
return $file;
}
/*
* Return the path of the image this session uploaded earlier, or FALSE.
*/
function pending_upload() {
$name = $_SESSION['pending_upload'] ?? NULL;
if (!is_upload_name($name))
return FALSE;
$file = upload_dir() . '/' . $name;
return is_file($file) ? $file : FALSE;
}
/*
* URL that shows the pending image; the name part stops browsers from
* showing an older upload from their cache.
*/
function pending_upload_url() {
return '/display?pending=' . substr($_SESSION['pending_upload'] ?? '', 0, 8);
}