Christian pleasure.
I start with telling you: "sorry for my english but in years of studies i don't speak very well".
Anyway....
I've a website developed with design pattern MVC, in this website the user can upload php file and run his. For using this file uploaded, the user have an a token: example.com/home/profile/advance?token=XXXXXXXXXXXXXX
I would that this file uploaded from user can't interact with my server as if it were inside a virtual machine.
It possible?
I hope I explained myself.
That's absolute possible. Just append the user's token to your form uploader:
<?php
$token = 'xxx';
?>
<form action="home/profile/advance?token=<?php echo htmlentities(urlencode($token)); ?>" method="post" enctype="multipart/form-data">
<input type="file" name="fileToUpload" id="fileToUpload">
<input type="submit" value="Upload File" name="submit">
This will make a $_POST to home/profile/advance?token=xxx.
Note that a server-side generated token should only contain alphanumeric characters, but it's best to wrap it in htmlentities() and urlencode() to be safe.
However, note that allowing users to upload (and run) their own PHP files is a huge security risk! I would strongly recommend not allowing them to run PHP, but instead limiting allowed uploads to raw .txt files. There's several different vectors to consider here, so I would recommend implementing the following (courtesy of CertaiN, with minor modification):
<?php
header('Content-Type: text/plain; charset=utf-8');
try {
// Undefined | Multiple Files | $_FILES Corruption Attack
// If this request falls under any of them, treat it invalid.
if (
!isset($_FILES['upfile']['error']) ||
is_array($_FILES['upfile']['error'])
) {
throw new RuntimeException('Invalid parameters.');
}
// Check $_FILES['upfile']['error'] value.
switch ($_FILES['upfile']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException('No file sent.');
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException('Exceeded filesize limit.');
default:
throw new RuntimeException('Unknown errors.');
}
// You should also check filesize here.
if ($_FILES['upfile']['size'] > 1000000) {
throw new RuntimeException('Exceeded filesize limit.');
}
// DO NOT TRUST $_FILES['upfile']['mime'] VALUE !!
// Check MIME Type by yourself.
$finfo = new finfo(FILEINFO_MIME_TYPE);
if (false === $ext = array_search(
$finfo->file($_FILES['upfile']['tmp_name']),
array(
'txt' => 'text/plain',
),
true
)) {
throw new RuntimeException('Invalid file format.');
}
// You should name it uniquely.
// DO NOT USE $_FILES['upfile']['name'] WITHOUT ANY VALIDATION !!
// On this example, obtain safe unique name from its binary data.
if (!move_uploaded_file(
$_FILES['upfile']['tmp_name'],
sprintf('./uploads/%s.%s',
sha1_file($_FILES['upfile']['tmp_name']),
$ext
)
)) {
throw new RuntimeException('Failed to move uploaded file.');
}
echo 'File is uploaded successfully.';
} catch (RuntimeException $e) {
echo $e->getMessage();
}
Related
I am new to PHP. Currently I am learning how to do PHP file upload validation and following an implementation proposed in https://www.php.net/manual/en/features.file-upload.php
I created a HTML file upload form, send a POST request to process.php then if it passed the validation check, server will redirect to another page called file.php containing the link of the uploaded file for downloading.
Here is my code:
process.php
<?php
try {
if (!isset($_FILES['file']['error']) || is_array($_FILES['file']['error'])) {
throw new RuntimeException("Invalid parameters.");
}
switch ($_FILES['file']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException("No file uploaded.");
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException("File size exceeded the limit.");
default:
throw new RuntimeException("Unknown errors.");
}
if ($_FILES['file']['size'] > 3 * 1024 * 1024) {
//exceeded 3 MB
throw new RuntimeException("File size exceeded the limit.");
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
if (false === $ext = array_search(
$finfo->file($_FILES['file']['tmp_name']),
array(
'jpg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
),
true
)) {
throw new RuntimeException("File type not allowed.");
}
$destpath = __DIR__ . "/" . sprintf("uploads/%s.%s", sha1_file($_FILES['file']['tmp_name']), $ext);
if (!move_uploaded_file(
$_FILES['file']['tmp_name'],
$destpath
)) {
throw new RuntimeException("Failed to move uploaded file.");
}
header("Location: file.php");
exit();
} catch (RuntimeException $e) {
echo $e->getMessage();
}
?>
file.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<?php
include "process.php";
?>
<p><?php $destpath;?></p><br>
Download
</body>
</html>
Here are some problems:
The $destpath in file.php is supposed to contain the file path ($destpath is a variable in process.php, which I included in file.php) but it appears to be a variable containing nothing.
After header("Location: file.php) I called exit() method, but the "Invalid parameters." message still displayed. I don't know why it passed all validation check (because it redirected to file.php successfully) but it still displayed that message (which is for the first check). Also I called exit() but the echo $e->getMessage() in the catch block still got executed.
I created uploads folder in terminal, and the folder had the privilege 755. I have to change it to 777 in terminal so move_uploaded_file() in process.php can work properly (maybe because the scripts are executed by XAMPP, which is not the owner of the folder). Is there any common practice for this uploading procedure? Because the folder with 777 privilege is insecure, I want it to be 755 and the move_uploaded_file() still works. Currently I am using XAMPP on MacOS.
I am new to PHP, please bear with me. Thank you!
I'm trying to develop some templates for common PHP tasks I've been dealing with. One of which is a general file upload handler.
So far I'm using the following reusable code which seems to be working fine without any noticeable bug:
<?php
if ( !isset($_POST['submit']) ) {
goto page_content;}
if ( $_FILES['file_upload']['error']===4 ) {
echo 'No file uploaded';
goto page_content;}
if ( $_FILES['file_upload']['error']===1 || $_FILES['file_upload']['error']===2 ) {
echo 'File exceeds maximum size limit';
goto page_content;}
if ( $_FILES['file_upload']['error']!==0 ) {
echo 'Failed to upload the file';
goto page_content;}
if ( !is_uploaded_file($_FILES['file_upload']['tmp_name']) ) {
echo 'Failed to upload the file';
goto page_content;}
require_once('imageResize.php');
$err = imageResize($_FILES['file_upload']['tmp_name'], 'random.png' );
if ( $err !== 0 ) {
echo 'Invalid image format';
goto page_content;}
echo 'Image uploaded successfully';
page_content:
?>
<form action="filename.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="1000000">
<input type="file" name="file_upload" accept="image/*">
<input type="submit" name="submit">
</form>
Additional file imageResize.php:
<?php
// image resize
function imageResize($source, $target){
$size = getimagesize($source);
if ($size === false) {return 1;} // invalid image format
$sourceImg = #imagecreatefromstring(#file_get_contents($source));
if ($sourceImg === false) {return 2;} //invalid image format
$width = imagesx($sourceImg);
$height = imagesy($sourceImg);
$sidelenght = min($width,$height);
$targetImg = imagecreatetruecolor(100, 100);
imagecopyresampled($targetImg, $sourceImg, 0, 0, ($width-$sidelenght)/2, ($height-$sidelenght)/2, 100, 100, $sidelenght, $sidelenght);
imagedestroy($sourceImg);
imagepng($targetImg, $target);
imagedestroy($targetImg);
return 0;
}
?>
Some main characteristics of this code are:
provides messages for the most common errors that can happened during the upload process
it allows the client to upload an image file up to 1Mb size
resizes all images to a standard 100x100 px size
save all images to a standard PNG format
Questions
Does this code safe? Or are there any vulnerability that could be exploited by an malicious client? In this case, how to solve it?
To avoid several nested IF-THEN-ELSE conditions (which can become hard to read), I'm currently using GOTO (which can become a bad control structure practice). Is there a better alternative?
Any other idea to improve it?
Really, look into putting this code into functions (maybe even a class), and instead of goto's just use return. This will allow you to better structure and separate logic where it needs to be separated.
Look at this example:
function upload_image($file)
{
if( $err = check_error($file['error']) ) return $err;
if( !is_uploaded_file($file['tmp_name']) ) return 'Failed to upload the file';
$resize = imageResize($file['tmp_name'], 'random.png');
if( $resize !== 0 )
{
return 'Invalid image format';
}
return true;
}
For error checking look into using the switch function. It will be more organized (in my opinion).
I would also check for the numerical upload errors in a separate function, this will allow for the individual action to be easily distinguished.
function check_error($err)
{
if($err === 0)
{
return false; // no errors
}
$response = false;
switch($err)
{
case 1:
case 2:
$response = 'File exceeds maximum size limit';
break;
case 4:
$response = 'No file uploaded';
break;
default:
$response = 'Unkown error';
}
return $response;
}
Then just call the function and display an error at the top if any:
$upload = upload_image($_FILE['file_upload']);
if( $upload === true ):
echo 'Image uploaded successfully!';
else:
echo $upload;
?>
<form action="filename.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="1000000">
<input type="file" name="file_upload" accept="image/*">
<input type="submit" name="submit">
</form>
<?php endif; ?>
I am trying to upload a file to a server using AS3 and PHP. Here is my AS3 Code and then the PHP code. The folder I am trying to upload to is writable. and the file size is about 20Kb. The php script is on my server, and the flash file calls it.
var UPLOAD_URL: String ="linktophpscriptonMysite"
var fr: FileReference;
var request: URLRequest = new URLRequest();
request.url = UPLOAD_URL;
function startThis(): void {
fr = new FileReference();
fr.addEventListener(Event.SELECT, selectHandler);
fr.addEventListener(Event.OPEN, openHandler);
fr.addEventListener(ProgressEvent.PROGRESS, progressHandler);
fr.addEventListener(Event.COMPLETE, completeHandler);
fr.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
startUpload()
}
function startUpload(): void {
try {
var success: Boolean = fr.browse();
trace("success")
} catch (error: Error) {
trace("Unable to browse for files.", Error);
}
}
function progressHandler(event: ProgressEvent): void {
trace(event.bytesLoaded, event.bytesTotal);
}
function ioErrorHandler(event: IOErrorEvent): void {
//trace("Some error ", event.target.data.systemResult);
//systemResult is echoed by PHP
}
function openHandler(event: Event): void {
try {
//var success: Boolean = fr.browse();
} catch (error: Error) {
trace("Unable to browse for files.", Error);
}
}
function completeHandler(event: Event): void {
trace(event.target.data.systemResult);
//this reads the result, again, from PHP echo "systemResult=all is good";
}
function selectHandler(event: Event): void {
fr.upload(request);
}
And then, here is the php code: This code is a general upload script I found on the php manual site
<?php
header('Content-Type: text/plain; charset=utf-8');
try {
// Undefined | Multiple Files | $_FILES Corruption Attack
// If this request falls under any of them, treat it invalid.
if (
!isset($_FILES['upfile']['error']) ||
is_array($_FILES['upfile']['error'])
) {
echo "systemResult=Error";
throw new RuntimeException('Invalid parameters.');
}
// Check $_FILES['upfile']['error'] value.
switch ($_FILES['upfile']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException('No file sent.');
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException('Exceeded filesize limit.');
default:
throw new RuntimeException('Unknown errors.');
}
// You should also check filesize here. max is 100 mb
if ($_FILES['upfile']['size'] > 10000000) {
throw new RuntimeException('Exceeded filesize limit.');
}
// DO NOT TRUST $_FILES['upfile']['mime'] VALUE !!
// Check MIME Type by yourself.
$finfo = new finfo(FILEINFO_MIME_TYPE);
if (false === $ext = array_search(
$finfo->file($_FILES['upfile']['tmp_name']),
array(
'jpg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
),
true
)) {
throw new RuntimeException('Invalid file format.');
}
// You should name it uniquely.
// DO NOT USE $_FILES['upfile']['name'] WITHOUT ANY VALIDATION !!
// On this example, obtain safe unique name from its binary data.
if (!move_uploaded_file(
$_FILES['upfile']['tmp_name'],
sprintf('./uploads/%s.%s',
sha1_file($_FILES['upfile']['tmp_name']),
$ext
)
)) {
throw new RuntimeException('Failed to move uploaded file.');
}
echo 'File is uploaded successfully.';
} catch (RuntimeException $e) {
echo $e->getMessage();
}
?>
The problem I am having is that the file does not get uploaded, and I dont get any feedback from php as to why.
Thank you for any help
UPDATE:
Thank you #akmozo for the reply and answer. Like I said in my comment, this script worked
<?php
$uploads_dir = './uploads/';
if( $_FILES['Filedata']['error'] == 0 ){
if( move_uploaded_file( $_FILES['Filedata']['tmp_name'], $uploads_dir.$_FILES['Filedata']['name'] ) ){
echo 'ok';
echo 'systemResult=Awesome';
exit();
}
}
echo 'error';
echo 'systemResult=did not work';
exit();
?>
By default, the upload data field name of a FileReference object is "Filedata" and that's what you should use in your PHP code ( $_FILES['Filedata'] ...).
You can of course change that name in the FileReference.upload() function :
fr.upload(request, 'upfile');
Hope that can help
Good day, I have been following a tutorial and have worked really hard to make this script work, I have managed to fix many things however the one constant problem I'm getting is:
No such file or directory in /home/xxxxxxx/public_html/regForm.php on line 93
What I am trying to acomplish is get user to upload a profile pic to my live server / website upon registration, here is part of my form and the code below:
I would greatly appreciate it if a more experienced user can give it a scan and advise to where I am going wrong, thanx in advance
HTML FORM
<form ENCTYPE="multipart/form-data" name="registration-form" id="regForm" method="post" action="regForm.php">
:
:
Upload Picture: <INPUT NAME="file_up" TYPE="file">
<input type="submit" value="Register" name="register" class="buttono" />
</form>
UPLOAD SCRIPT
$file_upload="true";
$file_up_size=$_FILES['file_up']['size'];
echo $_FILES['file_up']['name'];
if ($_FILES['file_up']['size']>250000){
$msg=$msg."Your uploaded file size is more than 250KB
so please reduce the file size and then upload.<BR>";
$file_upload="false";
}
if (!($_FILES['file_up']['type'] =="image/jpeg" OR $_FILES['file_up']['type'] =="image/gif"))
{
$msg=$msg."Your uploaded file must be of JPG or GIF. Other file types are not allowed<BR>";
$file_upload="false";
}
$file_name=$_FILES['file_up']['name'];
if($file_upload=="true"){
$ftp_server = "xxxxxx";
$ftp_conn = ftp_connect($ftp_server) or die("Could not connect to $ftp_server");
$ftp_username='xxxxxx';
$ftp_userpass='xxxxxx';
$login = ftp_login($ftp_conn, $ftp_username, $ftp_userpass);
$file = $file_name;
// upload file
if (ftp_put($ftp_conn, "public_html/img/userPics", $file, FTP_ASCII)) //here is the problem with the path I suspect
{
echo "Successfully uploaded $file.";
}
else
{
echo "Error uploading $file.";
}
// close connection
ftp_close($ftp_conn);
}
When dealing with images, you need to use a binary format.
FTP_BINARY instead of FTP_ASCII.
As per the manual:
http://php.net/manual/en/function.ftp-put.php
Make sure the path is correct. This can differ from different servers.
You also need a trailing slash here:
public_html/img/userPics/
^
Otherwise, your system will interpret that as:
userPicsIMAGE.JPG rather than the intended userPics/IMAGE.JPG
Yet, you may need to play around with the path itself. FTP is a bit tricky that way.
I.e.:
/home/xxxxxxx/public_html/userPics/
img/userPics/
../img/userPics/
Depending on the file's area of execution.
Root > ftp_file.php
-img
-userPics
The folder also needs to have proper permissions to be written to.
Usually 755.
Use error reporting if your system isn't already setup to catch and display error/notices:
http://php.net/manual/en/function.error-reporting.php
An example from the FTP manual:
<?php
ftp_chdir($conn, '/www/site/');
ftp_put($conn,'file.html', 'c:/wamp/www/site/file.html', FTP_BINARY );
?>
Another thing though, the use of of "true" and "false". You're most likely wanting to check for truthness
$file_upload=true;
rather than a string literal $file_upload="true";
same thing for
if($file_upload=="true")
to
if($file_upload==true)
and for $file_upload="false"; to check for falseness
to $file_upload=false;
Read the manual on this:
http://php.net/manual/en/language.types.boolean.php
This is the simplest type. A boolean expresses a truth value. It can be either TRUE or FALSE.
Test your FTP connection, pulled from https://stackoverflow.com/a/3729769/
try {
$con = ftp_connect($server);
if (false === $con) {
throw new Exception('Unable to connect');
}
$loggedIn = ftp_login($con, $username, $password);
if (true === $loggedIn) {
echo 'Success!';
} else {
throw new Exception('Unable to log in');
}
print_r(ftp_nlist($con, "."));
ftp_close($con);
} catch (Exception $e) {
echo "Failure: " . $e->getMessage();
}
Naming conventions:
I also need to note that on LINUX, userPics is not the same as userpics, should your folder name be in lowercase letters.
Unlike Windows, which is case-insensitive.
I have this code but this code is not allowing me to upload any types of video files.
This always shows 'Please Select File To Upload'.
Code for uploading file shown below:
$fileElementName = 'ufile';
if (!empty($_FILES[$fileElementName]['error'])) {
switch ($_FILES[$fileElementName]['error']) {
case '1':
$error = 'You Are Not Allowed To Upload File Of This Size';
break;
case '2':
$error = 'You Can Not Not Upload File Of This Size';
break;
case '3':
$error = 'The uploaded file was only partially uploaded';
break;
case '4':
$error = 'Not Able To Upload';
break;
case '6':
$error = 'Server Error Please Try Again Later';
break;
case '7':
$error = 'Failed to write file to disk';
break;
case '8':
$error = 'File upload stopped by extension';
break;
case '999':
default:
$error = 'Error Found But Did Not Found What Was Problem';
}
} elseif (empty($_FILES['ufile']['tmp_name']) || $_FILES['ufile']['tmp_name'] == 'none') {
$error = 'Please Select File To Upload';
$success = FALSE;
} else {
$file = $_FILES['ufile']['name'];
move_uploaded_file($_FILES["ufile"]["tmp_name"], $path);
}
echo json_encode($arr);
In the above code $path is upload/ which is correct--it works for all file except video.
This code always shows 'Please Select File To Upload'
Your error handling is broken. On uploads, a successful upload has error code 0 (UPLOAD_ERR_OK constant). You're doing
if (!empty(...))
empty() evaluates to boolean TRUE for zero values, so you're basically saying "if there was no error, act like there was one".
Your code really should be:
if ($_FILES['ufile']['error'] !== UPLOAD_ERR_OK) {
... error handling switch() here ...
die();
}
Your subsequent empty() checks on the tmp_name are also pointless. If no file was uploaded, then you'll get error code 4 (UPLOAD_ERR_NO_FILE) anyways.
Then for the move_uploaded_file() stuff, you pull out the uploaded filename into $file, but then move the file to $path, which is not set anywhere in your code. As a word of warning, never EVER use the user-supplied filename directly in file-system operations. It is trivial to embed path information in that field, and if you do not take appropriate sanitization/security precautions, you'll be allowing malicious users to scribble on any file on your server.
Did you defined the enctype of your form?
In the form tag insert the enctype as follow: enctype="multipart/form-data"
check permissions, if $path exists, etc, and change move_uploaded_file to copy, some servers have a restrict permission on folders
EDIT: Maybe same issue https://stackoverflow.com/a/3587158/1519436