I need to upload images to a remote HTTP server from a local script. The image name format is {32-char MD5}.jpg. Server directory format is 6c/d3/6cd3556deb0da54bca060b4c39479839.jpg. So, for example, if I upload the image called 6cd3556deb0da54bca060b4c39479839.jpg, it should be available at https://example.com/6c/d3/6cd3556deb0da54bca060b4c39479839.jpg.

The local script basically does a HTTPS multipart/form-data POST request with the "file" and "password" fields to a remote server (https://example.com/up.php).

Remote server has a PHP script (up.php) which checks if the "password" is correct and then either allows file uploading or disallows it.

Is this script secure? What can go wrong?

This is the code I use on the server. It checks password against SHA-512 hash and then checks if the filename extension is ".jpg".

<?php
//
// You need to post "file" and "password", which is checked
// against using SHA-512

// Check if the password is correct
$password_hash = "128-characters long SHA-512 string here"; if (hash("sha512",$_POST["password"]) != $password_hash) { http_response_code(403); echo "403 Access Denied"; exit(1); } if(!empty($_FILES["file"])) {
// Check if the filename extension is ".jpg",
$file_parts = pathinfo($_FILES["file"]["name"]);
if ($file_parts["extension"] != "jpg") { http_response_code(500); echo "Incorrect File Extension"; exit(1); } // Filename of the file being uploaded$filename = $_FILES["file"]["name"]; // Build a directory path$tmp_a = mb_substr($filename, 0, 2); // First 2 chars of a filename$tmp_b = mb_substr($filename, 2, 2); // 3 to 4 chars of a filename$tmp_c = $filename;$directory = "$tmp_a/$tmp_b";
// Full path to the file including the directory
$full_path = "$directory/$filename"; // If the directory doesn't exist, then create it if (!file_exists($directory)) {
mkdir($directory, 0755, true); } // Upload the file if (move_uploaded_file($_FILES["file"]["tmp_name"], \$full_path)) {
echo "Success";
} else {
http_response_code(500);
echo "Something went wrong";
exit(1);
}
}


• For comparing hashes like SHA, you should use hash_equals().

• If you are requiring the use of a password, you shouldn't use SHA-512. While it's certainly better than MD5, instead use bcrypt. Bcrypt is heavily recommended for password storage, especially long-term.

With bcrypt, instead of using hash() and hash_equals(), you would use password_hash() and password_verify().

• Should new directories really have read and execute permissions for the group and other users? Likewise, I would check the permissions of the file itself.

I would personally have the files as 0600 and directories as 0700 unless other permissions are needed.

• Checking the file extension is only helpful for preventing naive false uploads. On Linux and most UNIX-like operating systems, a file extension means very little (if anything). It could be any type of file with a JPEG extension, likewise anything could have a JPEG file extension.

I don't know about the rest of the code.

• Thanks for the reply, I have a few questions. 2. I have only one client (only my PC) and I plan to use only one password. It's very long and randomly generated. I connect only over HTTPS. Should I still bother with bcrypt and salt or SHA-512 will be enough? 4. Is checking both the file extension and MIME type of the file will be sufficient? – user9593274 Dec 20 '18 at 3:28
• Yes, I still think you should use bcrypt. It adds extra security with very little additional work. password_hash() handles the salting automatically, so no extra work is needed for that. Even if you're the only client, that doesn't keep others from trying to connect to your remote server. If you're the only client, I would also set up the server's firewall to reflect that (but that's a separate topic). I would just skip the file type checking -- if your password check works then it should be sufficient. MIME types and file extensions can be spoofed and provide no inherent security. – esote Dec 20 '18 at 4:44