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:
@@ -13,3 +13,7 @@ RECAPTCHA_SECRET=
|
|||||||
|
|
||||||
# Google Analytics tracking ID. Leave it empty to turn analytics off.
|
# Google Analytics tracking ID. Leave it empty to turn analytics off.
|
||||||
GOOGLE_ANALYTICS_ID=
|
GOOGLE_ANALYTICS_ID=
|
||||||
|
|
||||||
|
# Directory for uploaded images waiting to be saved. Keep it outside the
|
||||||
|
# web root. The default is xxx-uploads in the system temporary directory.
|
||||||
|
UPLOAD_DIR=
|
||||||
|
|||||||
+19
-1
@@ -14,7 +14,25 @@ if (isset($_SESSION['editor'])) {
|
|||||||
$is_admin = FALSE;
|
$is_admin = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
display_img(get_id());
|
if (isset($_GET['pending']))
|
||||||
|
display_pending_img();
|
||||||
|
else
|
||||||
|
display_img(get_id());
|
||||||
|
|
||||||
|
// Show the image this session uploaded at the preview step
|
||||||
|
function display_pending_img() {
|
||||||
|
$file = pending_upload();
|
||||||
|
$info = $file ? getimagesize($file) : FALSE;
|
||||||
|
if (!$info) {
|
||||||
|
header("HTTP/1.1 404 Not Found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
header("Content-type: ".$info['mime']);
|
||||||
|
header("Content-length: ".filesize($file));
|
||||||
|
header("X-Content-Type-Options: nosniff");
|
||||||
|
header("Cache-Control: private, no-store");
|
||||||
|
readfile($file);
|
||||||
|
}
|
||||||
|
|
||||||
function display_img($id) {
|
function display_img($id) {
|
||||||
global $db, $is_admin, $queue;
|
global $db, $is_admin, $queue;
|
||||||
|
|||||||
@@ -350,3 +350,51 @@ function which_table($move = FALSE, $action = 'read') {
|
|||||||
return 'xxx';
|
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);
|
||||||
|
}
|
||||||
|
|||||||
+7
-1
@@ -1,4 +1,10 @@
|
|||||||
<?php require_once("../session.lib.php"); ?>
|
<?php
|
||||||
|
if ( ! isset($_SERVER['HTTPS'])) {
|
||||||
|
header('Location: https://' . $_SERVER["SERVER_NAME"] . $_SERVER['REQUEST_URI']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
require_once("../session.lib.php");
|
||||||
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
|||||||
+17
-29
@@ -152,45 +152,33 @@ function get_mimetype($image_file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function save_uploaded_file() {
|
function save_uploaded_file() {
|
||||||
// Save uploaded file to new, temporary location
|
// A new upload in this request
|
||||||
if (is_uploaded_file($_FILES['upload']['tmp_name'] ?? '')) {
|
if (is_uploaded_file($_FILES['upload']['tmp_name'] ?? '')) {
|
||||||
$tmp_file = $_FILES['upload']['tmp_name'];
|
return store_upload($_FILES['upload']['tmp_name']);
|
||||||
$new_file = $_SERVER['DOCUMENT_ROOT'] . "/graphics" . $tmp_file;
|
}
|
||||||
$end_dir = dirname($new_file);
|
// An image uploaded at the preview step; the session knows which one
|
||||||
if (!is_dir($end_dir))
|
if (($_POST['uploaded'] ?? '') === 'pending') {
|
||||||
mkdir($end_dir, "0644", 1);
|
return pending_upload();
|
||||||
$image_file = $new_file;
|
|
||||||
move_uploaded_file($tmp_file, $image_file);
|
|
||||||
|
|
||||||
// File has already been uploaded, get location
|
|
||||||
} else if (isset($_POST['uploaded']) &&
|
|
||||||
is_file($_SERVER['DOCUMENT_ROOT'] . $_POST['uploaded'])) {
|
|
||||||
$image_file = realpath($_SERVER['DOCUMENT_ROOT'] . $_POST['uploaded']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($image_file)) {
|
|
||||||
return $image_file;
|
|
||||||
} else {
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function prune_images($image_file) {
|
function prune_images($image_file) {
|
||||||
// Delete image from filesystem, and cleanup anything older than $age
|
// Delete the saved image, and anything older than $age.
|
||||||
$age = '86400'; // a day old
|
// Only ever touch upload files inside the upload directory.
|
||||||
$cleanup_dir = dirname($image_file);
|
$age = 86400; // a day old
|
||||||
unlink($image_file);
|
$dir = upload_dir();
|
||||||
|
if (dirname($image_file) === $dir && is_upload_name(basename($image_file))) {
|
||||||
|
@unlink($image_file);
|
||||||
|
}
|
||||||
|
unset($_SESSION['pending_upload']);
|
||||||
|
|
||||||
if ( $dir_handle = @opendir($cleanup_dir) ) {
|
foreach (glob($dir . '/*') as $cleanup_file) {
|
||||||
while (($cleanup_file = readdir ($dir_handle)) !== FALSE) {
|
if (is_upload_name(basename($cleanup_file)) &&
|
||||||
if ($cleanup_file == '.' || $cleanup_file == '..') continue;
|
@filemtime($cleanup_file) < (time() - $age)) {
|
||||||
$cleanup_file = $cleanup_dir . "/". $cleanup_file;
|
|
||||||
if (@filemtime($cleanup_file) < (time() - $age)) {
|
|
||||||
@unlink($cleanup_file);
|
@unlink($cleanup_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir($dir_handle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -23,14 +23,13 @@ if (isset($_POST['g-recaptcha-response'])) {
|
|||||||
|
|
||||||
echo '<p>Please review your submission. Once you\'ve finished, please click "Submit" at the bottom of the page, or click "Preview" to check it again.</p>';
|
echo '<p>Please review your submission. Once you\'ve finished, please click "Submit" at the bottom of the page, or click "Preview" to check it again.</p>';
|
||||||
|
|
||||||
if (is_uploaded_file($_FILES['upload']['tmp_name'] ?? '') &&!isset($validate['upload'])) {
|
// Keep a new image outside the web root until the story is saved;
|
||||||
$tmp_file = $_FILES['upload']['tmp_name'];
|
// display.php shows it to this session only.
|
||||||
$upload = getimagesize($tmp_file);
|
if (is_uploaded_file($_FILES['upload']['tmp_name'] ?? '') && !isset($validate['upload'])) {
|
||||||
$new_file = $_SERVER['DOCUMENT_ROOT'] . "/graphics" . $tmp_file;
|
if (store_upload($_FILES['upload']['tmp_name']))
|
||||||
move_uploaded_file($tmp_file, $new_file);
|
$file_src = pending_upload_url();
|
||||||
$file_src = "/graphics" .$tmp_file;
|
} else if (isset($uploaded) && $uploaded === 'pending' && pending_upload() && !$remove_img) {
|
||||||
} else if (isset($uploaded) && is_file($_SERVER['DOCUMENT_ROOT'] . $uploaded) && !$remove_img) {
|
$file_src = pending_upload_url();
|
||||||
$file_src = $uploaded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
echo display_stories();
|
echo display_stories();
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ echo (isset($story) ? $story : '')
|
|||||||
<?php
|
<?php
|
||||||
if (isset($file_src) || isset($img)) {
|
if (isset($file_src) || isset($img)) {
|
||||||
if (isset($file_src)) {
|
if (isset($file_src)) {
|
||||||
echo ' <input type="hidden" id="uploaded" name="uploaded" value="'.$file_src.'" />';
|
echo ' <input type="hidden" id="uploaded" name="uploaded" value="pending" />';
|
||||||
} else {
|
} else {
|
||||||
echo ' <input type="hidden" id="uploaded" name="uploaded" value="'.$id.'" />';
|
echo ' <input type="hidden" id="uploaded" name="uploaded" value="'.$id.'" />';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user