Jump to content

Can't get login and file uploads to work


tottedaman
Go to solution Solved by jodunno,

Recommended Posts

Hi

So I'm pretty new to php and web development in general and I'm quiet stuck in my first project. The idea of my project is to make a simple login system where you can sign in with a user account and then get to a file upload where you can submit files to the server and user some jquery to make it look nice. I've gotten the login system to work as I want it to with connection to a mysql db and all. But I can't for the life of me get file uploads to work with my login system. It keeps saying that _FILES is empty and that a array key is undefined. But the weird part is that if I simply skip the entire login system and create just a simple HTML-form with a upload button and a seperate php-file then it works perfectly but as soon as I try to use that code in my project (after you get logged in using the login system) it just won't work and I can't for the life of me figure out what's wrong, my code seams fine and if I put the "simple files" in the same directory on the web server as my whole project the file uploads works. My code is quiet long an there's a few files, I will post them down below.

Link to comment
Share on other sites

you forgot to close the if condition in the login upload php file:

<?php 
session_start();
if (isset($_SESSION['id']) && isset($_SESSION['user_name'])) { 

somewhere over the rainbow:

<?php } ?>

i recommend switching the request method and checking $_POST with empty over isset (simply typing a zero passes isset)
also change your logic from if...elseif..else to if alone

switch ($_SERVER['REQUEST_METHOD']) {
  case 'GET': //get lost code or friendly page
  break;
  case 'POST':
    if (empty($_POST['username'])) { /*set error and leave or just leave + exit; etc.*/ }
    if (empty($_POST['password'])) { /*set error and leave or just leave + exit; etc.*/ }
    /*rest of your code*/
  break;
}

i recommend hashing passwords and using hash_equals to verify passwords
i recommend storing the sha1 value of usernames and comparing hashes versus plaintext
i also recommend using pdo for database interaction

good luck...

Link to comment
Share on other sites

2 hours ago, tottedaman said:

Hi

So I'm pretty new to php and web development in general and I'm quiet stuck in my first project. The idea of my project is to make a simple login system where you can sign in with a user account and then get to a file upload where you can submit files to the server and user some jquery to make it look nice. I've gotten the login system to work as I want it to with connection to a mysql db and all. But I can't for the life of me get file uploads to work with my login system. It keeps saying that _FILES is empty and that a array key is undefined. But the weird part is that if I simply skip the entire login system and create just a simple HTML-form with a upload button and a seperate php-file then it works perfectly but as soon as I try to use that code in my project (after you get logged in using the login system) it just won't work and I can't for the life of me figure out what's wrong, my code seams fine and if I put the "simple files" in the same directory on the web server as my whole project the file uploads works. My code is quiet long an there's a few files, I will post them down below.

index.php

<!DOCTYPE html>

<html>

<head>
   <meta name="viewport" content="initial-scale=1, maximum-scale=1">
  <title>Startsida</title>
    
  <link href="jquery-mobile/jquery.mobile.theme-1.0.min.css" rel="stylesheet" type="text/css">
  <link href="jquery-mobile/jquery.mobile.structure-1.0.min.css" rel="stylesheet" type="text/css">
  
  <script src="jquery-mobile/jquery-1.6.4.min.js" type="text/javascript"></script>
  <script src="jquery-mobile/jquery.mobile-1.0.min.js" type="text/javascript"></script>

</head>

<body>

<div data-role="page" id="page">

  <div data-role="header">
  
   <h1>Selct a option</h1>
    
  </div>
  
  <div data-role="content">
  
  <a href="upload_form.php" data-role="button">Upload</a>
  
  </div>
 
  </div>

</body>

</html>

upload_form.php

<!DOCTYPE html>

<html>

 <head>
 
  <meta name="viewport" content="initial-scale=1, maximum-scale=1">
  <title>Sign in</title>
    
  <link href="jquery-mobile/jquery.mobile.theme-1.0.min.css" rel="stylesheet" type="text/css">
  <link href="jquery-mobile/jquery.mobile.structure-1.0.min.css" rel="stylesheet" type="text/css">
  
  <script src="jquery-mobile/jquery-1.6.4.min.js" type="text/javascript"></script>
  <script src="jquery-mobile/jquery.mobile-1.0.min.js" type="text/javascript"></script>

 </head>

<body>

<div data-role="page" id="page">

  <div data-role="header">
  
   <h1>Sign in</h1>
   
    <a href="index.php" data-role="button" data-icon="home">Back to start</a>
	
  </div>
  
  
  <div data-role="content">
  
   <form action="login_upload.php" method="post" enctype="multipart/form-data">

        <?php if (isset($_GET['error'])) { ?>

            <p class="error"><?php echo $_GET['error']; ?></p>

        <?php } ?>

        <label>Username:</label>

        <input type="text" name="uname"><br>

        <label>Password:</label>

        <input type="password" name="password"><br> 

        <button type="submit">Sign in</button>

     </form>
    
  </div>
  
</div>

</body>

</html>

login_upload.php

<?php 

session_start(); 

include "db_conn.php";

if (isset($_POST['uname']) && isset($_POST['password'])) {

    function validate($data){

       $data = trim($data);

       $data = stripslashes($data);

       $data = htmlspecialchars($data);

       return $data;

    }

    $uname = validate($_POST['uname']);

    $pass = validate($_POST['password']);

    if (empty($uname)) {

        header("Location: upload_form.php?error=Username is required!");

        exit();

    }else if(empty($pass)){

        header("Location: upload_form.php?error=Password is required!");

        exit();

    }else{

        $sql = "SELECT * FROM users WHERE user_name='$uname' AND password='$pass'";

        $result = mysqli_query($conn, $sql);

        if (mysqli_num_rows($result) === 1) {

            $row = mysqli_fetch_assoc($result);

            if ($row['user_name'] === $uname && $row['password'] === $pass) {

                echo "Logged in!";

                $_SESSION['user_name'] = $row['user_name'];

                $_SESSION['id'] = $row['id'];

                header("Location: upload_home.php");

                exit();

            }else{

                header("Location: upload_form.php?error=Incorrect username or password");

                exit();

            }

        }else{

            header("Location: upload_form.php?error=Incorrect username or password");

            exit();

        }

    }

}else{

    header("Location: index.php");

    exit();

}

upload_home.php

<?php 

session_start();

if (isset($_SESSION['id']) && isset($_SESSION['user_name'])) {

 ?>

<!DOCTYPE html>

<html>

 <head>

  <title>Upload files</title>
    
  <link href="jquery-mobile/jquery.mobile.theme-1.0.min.css" rel="stylesheet" type="text/css">
  <link href="jquery-mobile/jquery.mobile.structure-1.0.min.css" rel="stylesheet" type="text/css">
  
  <script src="jquery-mobile/jquery-1.6.4.min.js" type="text/javascript"></script>
  <script src="jquery-mobile/jquery.mobile-1.0.min.js" type="text/javascript"></script>

 </head>

<body>

<div data-role="page" id="page">
  <div data-role="header">
   <h1>Upload files</h1>
   <a href="logout.php" data-role="button" data-icon="home">Sign out</a>
   
  </div>
  
  
  <div data-role="content">
  
  <form action="fileUpload.php" method="post" enctype="multipart/form-data">
        <input type="file" name="Upload">
        <input type="submit">
    </form>

  </div>
 
  </div>



</body>

</html>

<?php 

}else{

     header("Location: index.php");

     exit();

}

 ?>

db_conn.php

<?php

$sname= "localhost";

$unmae= "My username for mysql";

$password = "My password för mysql";

$db_name = "fildelning";

$conn = mysqli_connect($sname, $unmae, $password, $db_name);

if (!$conn) {

    echo "Connection failed";

}

fileUpload.php

<html>

<head>

<title>Fileupload</title>

</head>

<body>

<?php

$dir = "./folder/";
$timestamp = time();
$filename = $dir.$timestamp.basename($_FILES['Upload']['name']);

var_dump($_FILES);

echo "<br><br>";

if (move_uploaded_file($_FILES['Upload']['tmp_name'], $filename)){
    echo "<p>File was uploaded --> ".$_FILES['Upload']['name'];
} else {
    echo "Upload failed".$_FILES['Upload']['name'];
}

echo "<p>Information about file from $ FILE array</p>";
echo "File Name: ".$_FILES['Upload']['name']."<br>";
echo "File Type: ".$_FILES['Upload']['type']."<br>";
echo "File Size: ".$_FILES['Upload']['size']."kB<br>";
?>

</body>

</html>

fileUpload.html

<html>

<body>

    <h1>File Upload Form</h1>

    <form action="fileUpload.php" method="post" enctype="multipart/form-data">
        <input type="file" name="Upload">
        <input type="submit">
    </form>
	
</body>

</html>

 

Link to comment
Share on other sites

Hello again, i have copied the two files named upload_home.php and fileUpload.php to my xampp installation. I altered the code a bit with request method and tried he form. The path set in the dir variable was incorrect. I used a folder named uploads and placed a folder in the uploads folder named folder and the code works for me.

upload.php

<html>
<head>
  <title>Upload files</title>
</head>
<body>

<div data-role="page" id="page">
  <div data-role="header">
   <h1>Upload files</h1>
   <a href="logout.php" data-role="button" data-icon="home">Sign out</a>
   </div>

  <div data-role="content">
  
  <form action="/upload/fileUpload.php" method="post" enctype="multipart/form-data">
        <input type="file" name="Upload">
        <input type="submit">
    </form>

  </div>
  </div>

</body></html>

fileUpload.php

<html>
<head>
<title>Fileupload</title>
</head>
<body>

<?php

if ($_SERVER['REQUEST_METHOD'] === 'POST') { 
   $dir = "folder/";
   $timestamp = time();
   $filename = $dir.$timestamp.basename($_FILES['Upload']['name']);

   var_dump($_FILES);

   echo '<br><br>';

   if (move_uploaded_file($_FILES['Upload']['tmp_name'], $filename)){
      echo '<p>File was uploaded --> '.$_FILES['Upload']['name']);
   } else {
      echo 'Upload failed'.$_FILES['Upload']['name']);
   }

   echo '<p>Information about file from $FILE array</p>';
   echo 'File Name: '.$_FILES['Upload']['name'].'<br>';
   echo 'File Type: '.$_FILES['Upload']['type'].'<br>';
   echo 'File Size: '.$_FILES['Upload']['size'].'kB<br>';
}
?>

</body></html>

so it seems as though your path to folder is incorrect. You should enable php errors because it will show you a 'cannot move to folder' message.

  • Great Answer 1
Link to comment
Share on other sites

you need to put the form processing code and the corresponding form on the same page. what you have now takes almost two times the amount of code and by passing messages through the url, it is open to phishing attacks and cross site scripting.

the code for any page should be laid out in this general order -

  1. initialization
  2. post method form processing
  3. get method business logic - get/produce the data needed to display the page
  4. html document

here's a list of points that will help you with your code -

  1. validate all resulting web pages at validator.w3.org
  2. since you will be putting the form and the form processing code on the same page, you can leave the entire action='...' attribute out of the form tag to cause the form to submit to the same page it is on.
  3. if you put the <label></label> tags around the form field they belong with, you can leave out the for='...' and corresponding id='...' attributes, which you don't have anyways.
  4. use 'require' for things your code must have for it to work.
  5. if you are building multiple web pages, use 'require' for the common parts so that you are not repeating code over and over.
  6. the post method form processing code should first detect if a post method form was submitted. in your current code, writing out a series of isset() tests, you are creating bespoke code that must be changed every time you do something different. also, if you had a form with 30 or a 100 fields, would writting out a series of 30 or a 100 isset() statements seem like a good use of your time?
  7. forget about this validate() function. it is improperly named and the only thing it is doing that is proper is trimming the data.
  8. don't create a bunch of discrete variables for nothing. instead keep the form data as a set in a php array variable, then operate on elements in this array variable throughout the rest of the code.
  9. your post method form processing code should trim, than validate all input data, storing user/validation errors in an array using the field name as the array index. after the end of all the validation logic, if there are no errors (the array will be empty), use the submitted form data.
  10. the only redirect you should have in your code should be upon successful completion of the post method form processing and it should be to the exact same url of the current page to cause a get request for that page.
  11. you should not store plain text passwords. use php's password_hash() and password_verify()
  12. you cannot successfully echo or output content before a header() redirect.
  13. the only value that you should store in a session variable upon successful login is the user's id. you should query on each page request to get any other user information, such as their username or permissions.
  14. if there are user/validation errors, and since you are now putting the form and the form processing code on the same page, your code would continue on to display the html document, display any errors, re-display the form, populating any appropriate fields with there existing values so that the user doesn't need to keep reentering data over and over.
  15. any dynamic value that you output in a html context, should have htmlentities() applied to it when you output it, to help prevent cross site scripting.
  16. when conditional failure code is much shorter then the success code, if you complement the condition being tested and put the failure code first, it is easier to follow what your code is doing. also, any time you have an exit/die in a conditional branch of code, you don't need to make any following branch 'conditional' since exaction won't continue past that point.
  17. don't let visitors (and hackers) know when internal errors have occurred, such as database connection errors. instead, use exceptions for errors and in most cases simply let php catch and handle any exception, where php will use its error related settings to control what happens with the actual error information. also, in the latest php versions, a mysqli connection error automatically uses exceptions, so your existing connection error handling logic will never get executed upon an error and might as well be removed, simplifying the code.
  18. the file upload form processing must detect if the $_FILES array is not empty before referencing any of the $_FILES data. if total posted form data exceeds the post_max_size setting, both the $_POST and $_FILES arrays will be empty, you must detect this condition and setup a message for the user that the size of the uploaded file was too large. i suspect this is the reason why you are getting an empty $_FILES array and the undefined array index error.
  19. after you have detected that there is data in $_FILES, you must test the ['error'] element. there will only be valid data in the other elements if the error element is a zero (UPLOAD_ERR_OK) you should also setup user messages for the other errors that the user has control over. you can find the definition of the upload errors in the php documentation.
  • Great Answer 1
Link to comment
Share on other sites

  • Solution

so i had some free time to code a better example page for you while preserving some of your original content. I strongly disagree that you need to submit the form to the same page [rolling my eyes] but do whatever feels best for you.

<?php declare (strict_types = 1);
  // if (empty($_SESSION['userID']) { header("Location: /"); exit; }
  (string) $SID_page = 'getForm'; (string) $SID_pageTitle = 'Upload files';
  switch ($_SERVER["REQUEST_METHOD"]) {
    case 'POST':
         (string) $SID_errors = '';
         (array) $SID_filesArrayErrors = ['4'=>'Please choose a file for upload']; 
         if (!empty($_FILES['Upload']['error'])) { if (array_key_exists($_FILES['Upload']['error'], $SID_filesArrayErrors)) { $SID_errors = $SID_filesArrayErrors[$_FILES['Upload']['error']]; break; } $SID_errors = 'fileArray'; break; }
         if (preg_match('/^[0-9A-Za-z-_.\s]{1,64}.[jpg|jpeg|jpe|png|gif]+$/', $_FILES['Upload']['name']) === 0) { $SID_errors = 'Invalid filename (Filenames must be Alphanumeric with the following acceptions: - _ . and word spaces)'; break; }
         if (function_exists(pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME))) { /* log this error */ $SID_errors = 'hack attempt'; break; }
         /* catches built-in functions: phpinfo, phpinfo(), file_get_contents et cetera. use prefixes for all user defined code (here $SID_, stands for site id) */
         $SID_dir = 'folder/';
         $SID_timestamp = time();
         $SID_filename = $SID_dir . $SID_timestamp.basename($_FILES['Upload']['name']);
         move_uploaded_file($_FILES['Upload']['tmp_name'], $SID_filename);
         $SID_page = 'postForm'; $SID_pageTitle = 'Files upload';
    break;
  }
?>
<html>
<head>
  <title><?php if (!empty($SID_pageTitle)) { echo $SID_pageTitle; } ?></title>
</head>
<body>
<?php switch ($SID_page) { case 'getForm': ?>

<div data-role="page" id="page">
  <div data-role="header">
   <h1>Upload files</h1>
   <a href="logout.php" data-role="button" data-icon="home">Sign out</a>
   </div>

  <div data-role="content">

<?php if (!empty($SID_errors)) { echo '<div><p>' . $SID_errors . '</p></div>'; } ?>


  <form method="post" enctype="multipart/form-data">
        <input type="file" name="Upload">
        <input type="submit">
    </form>

  </div>
  </div>

<?php break; case 'postForm':
  /* var_dump($_FILES); */
  echo '<p>File was uploaded --> '. htmlspecialchars(urlencode($_FILES['Upload']['name']), ENT_QUOTES);
  echo '<br>';
  echo '<p>Information about file from $FILE array</p>';
  echo 'File Name: ' . htmlspecialchars(urlencode($_FILES['Upload']['name']), ENT_QUOTES) . '<br>';
  echo 'File Type: ' . htmlspecialchars($_FILES['Upload']['type'], ENT_QUOTES) . '<br>';
  echo 'File Size: ' . htmlspecialchars($_FILES['Upload']['size'], ENT_QUOTES) . 'kB<br>';

  /* one could add the form here too for more uploads but a user limit should be placed in a database
     then loaded into the session at login or retrieved from the db on pageload */
 break; } ?>

</body>
</html>

good luck to you. You may want to ask requinix about my regex code. I am not a programmer either, so perhaps my regex could be better. Have a nice day.

Link to comment
Share on other sites

1 hour ago, jodunno said:

so i had some free time to code a better example page for you while preserving some of your original content. I strongly disagree that you need to submit the form to the same page [rolling my eyes] but do whatever feels best for you.

<?php declare (strict_types = 1);
  // if (empty($_SESSION['userID']) { header("Location: /"); exit; }
  (string) $SID_page = 'getForm'; (string) $SID_pageTitle = 'Upload files';
  switch ($_SERVER["REQUEST_METHOD"]) {
    case 'POST':
         (string) $SID_errors = '';
         (array) $SID_filesArrayErrors = ['4'=>'Please choose a file for upload']; 
         if (!empty($_FILES['Upload']['error'])) { if (array_key_exists($_FILES['Upload']['error'], $SID_filesArrayErrors)) { $SID_errors = $SID_filesArrayErrors[$_FILES['Upload']['error']]; break; } $SID_errors = 'fileArray'; break; }
         if (preg_match('/^[0-9A-Za-z-_.\s]{1,64}.[jpg|jpeg|jpe|png|gif]+$/', $_FILES['Upload']['name']) === 0) { $SID_errors = 'Invalid filename (Filenames must be Alphanumeric with the following acceptions: - _ . and word spaces)'; break; }
         if (function_exists(pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME))) { /* log this error */ $SID_errors = 'hack attempt'; break; }
         /* catches built-in functions: phpinfo, phpinfo(), file_get_contents et cetera. use prefixes for all user defined code (here $SID_, stands for site id) */
         $SID_dir = 'folder/';
         $SID_timestamp = time();
         $SID_filename = $SID_dir . $SID_timestamp.basename($_FILES['Upload']['name']);
         move_uploaded_file($_FILES['Upload']['tmp_name'], $SID_filename);
         $SID_page = 'postForm'; $SID_pageTitle = 'Files upload';
    break;
  }
?>
<html>
<head>
  <title><?php if (!empty($SID_pageTitle)) { echo $SID_pageTitle; } ?></title>
</head>
<body>
<?php switch ($SID_page) { case 'getForm': ?>

<div data-role="page" id="page">
  <div data-role="header">
   <h1>Upload files</h1>
   <a href="logout.php" data-role="button" data-icon="home">Sign out</a>
   </div>

  <div data-role="content">

<?php if (!empty($SID_errors)) { echo '<div><p>' . $SID_errors . '</p></div>'; } ?>


  <form method="post" enctype="multipart/form-data">
        <input type="file" name="Upload">
        <input type="submit">
    </form>

  </div>
  </div>

<?php break; case 'postForm':
  /* var_dump($_FILES); */
  echo '<p>File was uploaded --> '. htmlspecialchars(urlencode($_FILES['Upload']['name']), ENT_QUOTES);
  echo '<br>';
  echo '<p>Information about file from $FILE array</p>';
  echo 'File Name: ' . htmlspecialchars(urlencode($_FILES['Upload']['name']), ENT_QUOTES) . '<br>';
  echo 'File Type: ' . htmlspecialchars($_FILES['Upload']['type'], ENT_QUOTES) . '<br>';
  echo 'File Size: ' . htmlspecialchars($_FILES['Upload']['size'], ENT_QUOTES) . 'kB<br>';

  /* one could add the form here too for more uploads but a user limit should be placed in a database
     then loaded into the session at login or retrieved from the db on pageload */
 break; } ?>

</body>
</html>

good luck to you. You may want to ask requinix about my regex code. I am not a programmer either, so perhaps my regex could be better. Have a nice day.

This code worked flawlessly! Thank you so much for taking the time to fix it! 

But I have one question, why is there filetypes jpg, jpeg, jpe etc defined in the if statement preg_match? I understand that the line is checking filenames but does it mean that uploads are limited to only those filetypes?

I tried uploading a pdf-file and that worked as well as a .png file. 

Limiting what type of files is allowed to be uploaded isn't really necessary in my case since the webserver its hosted on is only locally and I'm don't plan to make it accessible over the internet either. And I'm probably gonna use it the most myself. And is it any file size limitations specified in the code? I can control the maximum file size in the php.ini instead.

Link to comment
Share on other sites

I try to help out whenever i am able to do so. Regular php specialists answer alot of questions here so i try to give back to the community. I am not a programmer but i'm learning programming slowly. I know what it is like to be a beginner and the stress of trying to get code to work.

I apologize for the regex error. I sometimes copy and paste my regex expressions and tweak them to a new design. I forgot to remove the brackets. Try this code instead:

         

if (preg_match('/^[0-9A-Za-z-_.\s]{1,64}.jpg|jpeg|jpe|png|gif$/', $_FILES['Upload']['name']) === 0) { $SID_errors = 'Invalid file: Filenames must be Alphanumeric with the following acceptions: - _ . and word spaces. Filetypes must be jpg/jpeg, png or gif'; break; }

 

Requinix is a regex specialist here. If you have further troubles with regex, then post a thread in the regex subforum.

I'm sure that you notice some missing code, like session start. I assume that you know to add it in order to read from the session. Also, i have erroneously applied htmlspecialchars to the file size, which should be integer instead. Furthermore, you shouldn't alert an internet user about the function exists, just log it and either disallow the name or change it in the code while storing the original name for download purposes. You have mentioned that the site is to be local and not online, so i suppose you could just skip this code anyway.

I hope that you have a good day and please inform me of any other problems with my sample file. I may have overlooked some things.
 

Link to comment
Share on other sites

I have decided to update the sample page that i posted earlier. I shouldn't assume that you know what is missing. I've added the session start and even modified the output pages for a better experience.

<?php declare (strict_types = 1);
  //session_start();
  //if (empty($_SESSION['userID'])) { header("Location: /"); exit; }
  (string) $SID_page = 'getForm'; (string) $SID_pageTitle = 'Upload files';
  switch ($_SERVER["REQUEST_METHOD"]) {
    case 'GET':
       /* use this area to set any variables required for the get page, et cetera */
    break;
    case 'POST':
       (string) $SID_errors = '';
       (array) $SID_filesArrayErrors = ['4'=>'Please choose a file for upload']; 
       if (!empty($_FILES['Upload']['error'])) { if (array_key_exists($_FILES['Upload']['error'], $SID_filesArrayErrors)) { $SID_errors = $SID_filesArrayErrors[$_FILES['Upload']['error']]; break; } $SID_errors = 'Error uploading file. Please try again.'; break; }
       if (preg_match('/^[0-9A-Za-z-_.\s]{1,64}.jpg|jpeg|jpe|png|gif$/', $_FILES['Upload']['name']) === 0) { $SID_errors = 'Invalid file: Filenames must be Alphanumeric with the following acceptions: - _ . and word spaces. Filetypes must be jpg/jpeg, png or gif'; break; }
       if (function_exists(pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME))) { error_log("attention! filename contains a function name: " . pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME));  /* $SID_errors = 'hack attempt'; */ break; }
       /* catches built-in functions: phpinfo, phpinfo(), file_get_contents et cetera. use prefixes for all user defined code (here $SID_, stands for site id). */
       $SID_dir = 'folder/';
       $SID_timestamp = time();
       $SID_filename = $SID_dir . $SID_timestamp.basename($_FILES['Upload']['name']);
       move_uploaded_file($_FILES['Upload']['tmp_name'], $SID_filename);
       $SID_page = 'postForm'; $SID_pageTitle = 'Files upload';
    break;
  }
?>
<!DOCTYPE html>
<html>
<head>
  <title><?php if (!empty($SID_pageTitle)) { echo $SID_pageTitle; } ?></title>
</head>
<body>

<div data-role="page" id="page">
  <div data-role="header">
    <h1>Upload files</h1>
    <a href="logout.php" data-role="button" data-icon="home">Sign out</a>
  </div>
<?php switch ($SID_page) { case 'getForm': ?>

  <div data-role="content">
<?php if (!empty($SID_errors)) { echo '<div><p>' . $SID_errors . '</p></div>'; } ?>


    <form method="post" enctype="multipart/form-data">
      <input type="file" name="Upload" required>
      <input type="submit">
    </form>
  </div>

<?php break; case 'postForm':
  /* var_dump($_FILES); */
  echo '<p>File was uploaded --> '. htmlspecialchars(urlencode($_FILES['Upload']['name']), ENT_QUOTES);
  echo '<br>';
  echo '<p>Information about file from $FILE array</p>';
  echo 'File Name: ' . htmlspecialchars(urlencode($_FILES['Upload']['name']), ENT_QUOTES) . '<br>';
  echo 'File Type: ' . htmlspecialchars($_FILES['Upload']['type'], ENT_QUOTES) . '<br>';
  echo 'File Size: ' . $_FILES['Upload']['size'] . 'kB<br>';
?>
  <div data-role="content">
    <p>Would you like to upload another file?</p>

    <form method="post" enctype="multipart/form-data">
      <input type="file" name="Upload" required>
      <input type="submit">
    </form>
  </div>

<?php break; } ?>

</div>

</body>
</html>

 

Link to comment
Share on other sites

i have no experience with file uploading scripts and i cannot find any professional examples. I am aware of the problems that exist with handling file uploads, such as hidden code inside of images. I have spent alot of free time trying to make a better file upload script because i may want to use one myself in the future.

Anyway, i have come across alot of problems while trying to redesign this script. I have never used number_format and i have learned that it automatically rounds numbers, which i do not want when displaying kilobytes. I have tried to present the size info as it appears in the right-click file properties of a Windows system. I also discovered that url encoded file names pass straight by the regex. LOL. what happens here, i have no idea. %FE should not get by the regex but it does. So i suppose that my regex is not correct. I applied it to the path filename instead and it now catches %FE.

I also had a problem using getimagesize when a file is corrupted. I had to figure out a way to ignore the warning and move on. I've added error handling for such warnings because i think that extra data in a file is an exception. Also, i wanted a way to define a maximum size for an image based upon its dimensions. I have not factored gif images yet because gif images have frames and can also be animated. I will need to think about it before i have a solution. But jpeg and png images now have a max size (which also catches unnecessary data).

So i will now post my newest code. I've added a binary logged in user variable for testing purposes because i am not using a session. I have not thought of everything (obviously) but alot of potential problems are handled with this new script and it is a bit more secure. Again, i am not a programmer so i may be missing something. Like i said before, i cannot find any professional examples, so this is my own attempt to handle file uploads as a file upload amateur.

<?php declare (strict_types = 1);
  /* session_start(); */
  (int) $SESSIONuserID = 1;
  /* var $SESSIONuserID represents $_SESSION['userID'] for non session development purposes */
  (string) $SID_pageTitle = 'Upload files';
  switch ($_SERVER["REQUEST_METHOD"]) {
    case 'GET':
       /* use this area to set any variables required for the get page, et cetera */
    break;
    case 'POST':
       (string) $SID_errors = '';
       if (empty($SESSIONuserID)) { $SID_errors = 'Please login to upload files'; break; }
       if ($_FILES['Upload']['size'] > 10000000) { $SID_errors = 'File size too large'; break; }
          /* 1 MB = 1000000, 5 MB = 5000000 Bytes, 10 MB = 10000000, 15 MB = 15000000, 20 MB = 20000000 Bytes */
       (array) $SID_filesArrayErrors = ['1'=>'File size is too large','4'=>'Please choose a file for upload']; 
       if (!empty($_FILES['Upload']['error'])) { if (array_key_exists($_FILES['Upload']['error'], $SID_filesArrayErrors)) { $SID_errors = $SID_filesArrayErrors[$_FILES['Upload']['error']]; break; } $SID_errors = 'Error uploading file. Please try again.'; break; }
       if (preg_match('/^[A-Za-z0-9-_.\s]{1,48}$/', pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME)) === 0) { $SID_errors = 'Invalid file: Filenames must be Alphanumeric with the following acceptions: - _ . and word spaces.'; break; }
          /* beware of bom and url encoded strings. for example: %FE%FF%00%3C%00s%00c%00r%00i%00p%00t%00%3E%00a%00l%00e%00r%00t%00(%00%22%00P%000%00w%00n%00e%00d%00%22%00)%00;%00%3C%00%00s%00c%00r%00i%00p%00t%00%3E */
       if (preg_match('/^jpg|jpeg|jpe|png|gif$/', pathinfo($_FILES['Upload']['name'], PATHINFO_EXTENSION)) === 0) { $SID_errors = 'Invalid file: Filetypes must be jpg/jpeg, png or gif'; break; }
       //if (function_exists(pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME))) { error_log("attention! file upload name contains a function name: " . pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME)); /* store original name, assign new name */ break; }
          /* catches built-in functions: phpinfo, phpinfo(), file_get_contents et cetera. use prefixes for all user defined code (here $SID_, stands for site id). */
       if (class_exists(pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME))) { error_log("attention! file upload name contains a class name: " . pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME));  /* store original name, assign new name */ break; }
       if (file_exists(pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME))) { error_log("attention! file upload exists: " . pathinfo($_FILES['Upload']['name'], PATHINFO_FILENAME));  /* store original name, assign new name */ break; }

       (string) $SID_kilobytes = strval($_FILES['Upload']['size'] / 1024);
       list($SID_digit, $SID_decimal) = explode('.', $SID_kilobytes);
       $SID_kilobytes = $SID_digit . '.' . substr($SID_decimal, 0, 1); unset($SID_digit); unset($SID_decimal);
       set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context) { throw new ErrorException( $err_msg, 0, $err_severity, $err_file, $err_line ); }, E_WARNING);
       try { $SID_dimensions = getimagesize($_FILES['Upload']['tmp_name']); } catch (Exception $e) { $e->getMessage(); $SID_errors = 'File may be corrupted. Please try again'; restore_error_handler(); break; }
         /* corrupt files cause a warning. suppressing errors is being avoided with a try catch block instead */
       if (empty($SID_dimensions) || !is_array($SID_dimensions)) { $SID_errors = 'File may be corrupted. Please try again'; break; }
       if (empty($SID_dimensions[0]) || empty($SID_dimensions[1])) { $SID_errors = 'File may be corrupted. Please try again'; break; }
       if ($SID_dimensions[0] < 10 || $SID_dimensions[1] < 10) { $SID_errors = 'File dimensions must be at least 10px x 10px'; }
       if ($SID_dimensions[0] > 1200 || $SID_dimensions[1] > 1200) { $SID_errors = 'File dimensions must be at most 1200px x 1200px'; break; }

       (string) $SID_maxDim = ''; (string) $SID_maxDimKB = ''; (int)  $SID_channels = 4;
       switch (strtolower(pathinfo($_FILES['Upload']['name'], PATHINFO_EXTENSION))) { case 'png': $SID_channels = 6; break; }
       (int) $SID_maxDim = round(($SID_dimensions[0] * $SID_dimensions[1] * $SID_channels) / 8);
       (string) $SID_maxDimKB = strval($SID_maxDim / 1024);
       if (is_string($SID_maxDimKB)) { list($SID_maxDimDigit, $SID_maxDimDecimal) = explode('.', $SID_maxDimKB); }
       if ($_FILES['Upload']['size'] > $SID_maxDim) { $SID_errors = 'File size too large for these dimensions ' . $SID_dimensions[0] . ' x ' . $SID_dimensions[1] . '<br>The file may contain unnecessary data.'; break; }

       (string) $SID_dir = 'folder/';
       (int) $SID_timestamp = time();
       (string) $SID_filename = $SID_dir . $SID_timestamp.basename($_FILES['Upload']['name']);
       if (!move_uploaded_file($_FILES['Upload']['tmp_name'], $SID_filename)) { $SID_errors = 'Failed to upload the file. Please try again.'; break; }
       (string) $SID_dateCreated = date('l, F, Y') . ' ' . date('h:i:s');
       $SID_pageTitle = 'File uploaded';
    break;
  }
?>
<!DOCTYPE html>
<html>
<head>
  <title><?php if (!empty($SID_pageTitle)) { echo $SID_pageTitle; } ?></title>
</head>
<body>

<div data-role="page" id="page">
  <div data-role="header">
    <h1>Upload files</h1>
    <?php if (empty($SESSIONuserID)) { echo '<a href="#" data-role="button" data-icon="home">Sign in</a>'; } else { echo '<a href="#" data-role="button" data-icon="home">Sign out</a>'; } ?>

  </div>

  <div data-role="content"><p>
<?php if (!empty($SID_errors)) { echo '<div><p>' . $SID_errors . '</p></div>'; }
  if (empty($SID_errors) && !empty($SID_pageTitle) && $SID_pageTitle === 'File uploaded') {
    echo '<p>Image transfer complete:</p>';
    echo '<p>Image name: ' . htmlspecialchars($_FILES['Upload']['name'], ENT_QUOTES) . '<br>';
    echo 'Image Type: ' . htmlspecialchars($_FILES['Upload']['type'], ENT_QUOTES) . '<br>';
    if (!empty($SID_dimensions[0]) && !empty($SID_dimensions[1])) { echo 'Image Dimensions: ' . $SID_dimensions[0] . ' x ' . $SID_dimensions[1] . '<br>';
    echo 'Image Size: '; if (!empty($SID_kilobytes)) { echo $SID_kilobytes . ' KB (' . $_FILES['Upload']['size'] . ' bytes)<br>'; } else { echo $_FILES['Upload']['size'] . ' bytes<br>'; }
    echo 'Maximum size imposed: ' . $SID_maxDimDigit . '.' . substr($SID_maxDimDecimal, 0, 1) . ' KB (' . $SID_maxDim . ' bytes)<br>'; }
    if (!empty($SID_dateCreated)) { echo 'Date created: ' . $SID_dateCreated . '</p>'; }
    echo '<p>Would you like to upload another image?</p>';
} ?>


    <form autocomplete="off" accept-charset="UTF-8" method="post" enctype="multipart/form-data">
      <input type="file" name="Upload">
      <input type="submit">
    </form>

  </p></div>

</div>

</body>
</html>

I am not done with this concept yet but i will not post further updates in this thread. Old threads should be avoided. I do apologize to everyone here for my sloppy code in the previous posts.

Best of luck to you and to all beginners of php.

Link to comment
Share on other sites

Your code would be far less 'sloppy' if you wrote it one instruction/statement per line so that reading it would be easier.

if (empty($SID_errors) && !empty($SID_pageTitle) && $SID_pageTitle === 'File uploaded') 
{
	echo '<p>Image transfer complete:</p>';
	echo '<p>Image name: ' . htmlspecialchars($_FILES['Upload']['name'], ENT_QUOTES) . '<br>';
	echo 'Image Type: ' . htmlspecialchars($_FILES['Upload']['type'], ENT_QUOTES) . '<br>';
	if (!empty($SID_dimensions[0]) && !empty($SID_dimensions[1])) 
	{ 
		echo 'Image Dimensions: ' . $SID_dimensions[0] . ' x ' . $SID_dimensions[1] . '<br>';
		echo 'Image Size: '; 
		if (!empty($SID_kilobytes)) 
			echo $SID_kilobytes . ' KB (' . $_FILES['Upload']['size'] . ' bytes)<br>';
		else 
			echo $_FILES['Upload']['size'] . ' bytes<br>'; 
		echo 'Maximum size imposed: ' . $SID_maxDimDigit . '.' . substr($SID_maxDimDecimal, 0, 1) . ' KB (' . $SID_maxDim . ' bytes)<br>'; 
	}
}

 

Link to comment
Share on other sites

sloppy refers to my latest discoveries, for example:
i have failed to recognize that dividing bytes by 1024 may not produce a number with a decimal point. 😬 My code breaks at this variable with a php error message.

i have to add a new line of code to this file:

if (!str_contains($SID_kilobytes, '.')) { $SID_kilobytes.= '.0'; }


the same is true for the $SID_maxDimKB variable both must be checked before the list explode

i also failed to recognize that the ini file may not be configured for file uploads
my code breaks with please select an image for upload, which is an incorrect error message.
i have to add an ini check to alert a user that file_upload is disabled

if (empty(ini_get('file_uploads'))) { $SID_errors = 'File uploads are disabled.'; break;}


this, then, sould disable the form as well. so $SID_formDisplay binary switch 0|1 needs implemented.

I also failed to handle form tampering by posting multiple files.
very amateur error. i should have thought about this in my last code change

if (count($_FILES) > 1) { $SID_errors = 'File uploads may not contain multiple files.'; break; }

I have since learned that file size checking requires filesize function for accuracy. 
Thus, i have added a new variable:

$SID_fileSize = filesize($_FILES['Upload']['tmp_name']);

subsequent changes are required:

if ($SID_filesize > 1000000) { $SID_errors = 'File size is larger than 1 MB.'; break; }


et cetera

I have also updated the try catch code because the custom error handler throws an argument error in PHP 8.2

I have a long way to go in order to develop a tight, secure, well thought out file upload script.
I didn't realize that file uploads are such a difficult concept to handle
Files will also need to be scanned for malicious code.

Another thing that i notice is that a user should have a maximum amount of file uploads
plus, one also needs to check if the user has already uploaded the file.
One cannot be permitted to upload the same file a million times.

this is what i mean about sloppy concepts.
As long as i am able to spot what is missing or incorrect, then i will end up with a pretty fine script (someday, LOL)

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.