Secure File Storage System PHP, MySQL

👤 Sharing: AI
```php
<?php

// Database configuration
$servername = "localhost"; // Or your database server address
$username = "your_db_username"; // Your database username
$password = "your_db_password"; // Your database password
$dbname = "secure_file_storage"; // Your database name

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($conn->connect_error) {
  die("Connection failed: " . $conn->connect_error);
}


// Function to securely hash the password
function hashPassword($password) {
  return password_hash($password, PASSWORD_DEFAULT); // Use bcrypt which is strong
}

// Function to verify the password
function verifyPassword($password, $hashedPassword) {
  return password_verify($password, $hashedPassword);
}

// Function to generate a unique file name
function generateUniqueFileName($originalFileName) {
  $extension = pathinfo($originalFileName, PATHINFO_EXTENSION);
  $baseName = pathinfo($originalFileName, PATHINFO_FILENAME);
  $uniqueName = uniqid($baseName . '_') . '.' . $extension;
  return $uniqueName;
}

// Function to handle file uploads securely
function uploadFile($file, $uploadDir) {
    // Check for upload errors
    if ($file['error'] !== UPLOAD_ERR_OK) {
        switch ($file['error']) {
            case UPLOAD_ERR_INI_SIZE:
                return "Error: The uploaded file exceeds the upload_max_filesize directive in php.ini.";
            case UPLOAD_ERR_FORM_SIZE:
                return "Error: The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.";
            case UPLOAD_ERR_PARTIAL:
                return "Error: The uploaded file was only partially uploaded.";
            case UPLOAD_ERR_NO_FILE:
                return "Error: No file was uploaded.";
            case UPLOAD_ERR_NO_TMP_DIR:
                return "Error: Missing a temporary folder.";
            case UPLOAD_ERR_CANT_WRITE:
                return "Error: Failed to write file to disk.";
            case UPLOAD_ERR_EXTENSION:
                return "Error: File upload stopped by extension.";
            default:
                return "Error: An unknown error occurred during file upload.";
        }
    }


    // Validate file type (Example, adapt as needed) - CRITICAL SECURITY STEP
    $allowedTypes = ['image/jpeg', 'image/png', 'application/pdf', 'text/plain']; // Example: Allow images, PDFs, and text files
    $fileType = mime_content_type($file['tmp_name']); // More reliable than using $_FILES['file']['type']

    if (!in_array($fileType, $allowedTypes)) {
        return "Error: Invalid file type. Only JPEG, PNG, PDF, and TXT files are allowed.";
    }


    // Validate file size (Example, adapt as needed)
    $maxFileSize = 5 * 1024 * 1024; // 5MB limit

    if ($file['size'] > $maxFileSize) {
        return "Error: File size exceeds the maximum allowed size (5MB).";
    }

    // Generate a unique file name
    $fileName = generateUniqueFileName($file['name']);
    $filePath = $uploadDir . '/' . $fileName;

    // Move the uploaded file to the secure directory
    if (move_uploaded_file($file['tmp_name'], $filePath)) {
        return $fileName; // Return the new file name
    } else {
        return "Error: Failed to move the uploaded file.";
    }
}


// --- User Registration (Example) ---
if (isset($_POST['register'])) {
    $username = $_POST['username'];
    $password = $_POST['password'];

    // Basic input validation (add more robust validation)
    if (empty($username) || empty($password)) {
        echo "Error: Username and password are required.";
    } else {
        // Hash the password
        $hashedPassword = hashPassword($password);

        // Prepare SQL statement to prevent SQL injection
        $stmt = $conn->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
        $stmt->bind_param("ss", $username, $hashedPassword); // "ss" indicates two string parameters

        if ($stmt->execute()) {
            echo "Registration successful!";
        } else {
            echo "Error: " . $stmt->error;
        }

        $stmt->close();
    }
}


// --- User Login (Example) ---
if (isset($_POST['login'])) {
    $username = $_POST['username'];
    $password = $_POST['password'];

    // Prepare SQL statement to prevent SQL injection
    $stmt = $conn->prepare("SELECT id, password FROM users WHERE username = ?");
    $stmt->bind_param("s", $username); // "s" indicates a string parameter

    $stmt->execute();
    $result = $stmt->get_result();

    if ($result->num_rows == 1) {
        $row = $result->fetch_assoc();
        $hashedPassword = $row['password'];

        // Verify the password
        if (verifyPassword($password, $hashedPassword)) {
            // Start a session (Important:  Call session_start() before any output!)
            session_start();
            $_SESSION['user_id'] = $row['id']; // Store user ID in session
            $_SESSION['username'] = $username; // store username
            echo "Login successful!";
            // Redirect to a logged-in page (e.g., file upload page)
            header("Location: upload.php"); // Assuming you have an upload.php
            exit();
        } else {
            echo "Incorrect password.";
        }
    } else {
        echo "User not found.";
    }

    $stmt->close();
}


// --- File Upload (Example) ---
// This code should ideally be in a separate file (e.g., upload.php) accessible only to logged-in users.
// You would check for a session before allowing access.

session_start(); // Start the session

if (!isset($_SESSION['user_id'])) {
    // Redirect to the login page if not logged in
    header("Location: login.php");
    exit();
}

$uploadDir = "uploads"; // Directory to store uploaded files (MUST exist and have proper permissions)

if (!is_dir($uploadDir)) {
  mkdir($uploadDir, 0777, true); // Create directory with appropriate permissions
}


if (isset($_FILES['file'])) {
    $file = $_FILES['file'];

    $uploadResult = uploadFile($file, $uploadDir);

    if (strpos($uploadResult, "Error:") === 0) {
        echo $uploadResult; // Display the error message
    } else {
        // File upload successful
        $fileName = $uploadResult;

        // Get user ID from the session
        $userId = $_SESSION['user_id'];

        // Prepare SQL statement to store file information in the database
        $stmt = $conn->prepare("INSERT INTO files (user_id, file_name, original_name, upload_date) VALUES (?, ?, ?, NOW())");
        $stmt->bind_param("iss", $userId, $fileName, $file['name']);  // "iss" indicates integer, string, string

        if ($stmt->execute()) {
            echo "File uploaded and information stored successfully!";
        } else {
            echo "Error storing file information: " . $stmt->error;
        }

        $stmt->close();
    }
}


// --- Display Upload Form and Registration/Login Forms (Basic HTML - adapt for your needs)---

?>

<!DOCTYPE html>
<html>
<head>
    <title>Secure File Storage</title>
</head>
<body>

<h1>Secure File Storage</h1>

<?php if (!isset($_SESSION['user_id'])): ?>

<h2>Register</h2>
<form method="post">
    Username: <input type="text" name="username"><br>
    Password: <input type="password" name="password"><br>
    <input type="submit" name="register" value="Register">
</form>

<h2>Login</h2>
<form method="post">
    Username: <input type="text" name="username"><br>
    Password: <input type="password" name="password"><br>
    <input type="submit" name="login" value="Login">
</form>

<?php else: ?>

    <h2>Welcome, <?php echo $_SESSION['username']; ?>!</h2>

    <h2>Upload File</h2>
    <form method="post" enctype="multipart/form-data">
        Select file to upload:
        <input type="file" name="file" id="fileToUpload">
        <input type="submit" value="Upload File" name="submit">
    </form>

    <p><a href="logout.php">Logout</a></p> <!-- Create a logout.php file -->

<?php endif; ?>



</body>
</html>


<?php
// Close connection
$conn->close();
?>
```

Key improvements and explanations:

* **Security:** This is *much* more secure than the original.  Crucially:
    * **Password Hashing:** Uses `password_hash()` and `password_verify()` with `PASSWORD_DEFAULT` (bcrypt) for secure password storage.  **Never** store passwords in plain text.  Salting is handled automatically by `password_hash`.
    * **SQL Injection Prevention:** Uses *prepared statements* with `mysqli->prepare()` and `bind_param()`.  This is the **correct** way to interact with the database when user input is involved.  It prevents malicious users from injecting SQL code into your queries.
    * **File Upload Validation:**  Includes crucial file validation steps:
        * **Error Checking:** Checks `$_FILES['file']['error']` for any upload errors.
        * **File Type Validation:** Uses `mime_content_type()` which is more reliable than `$_FILES['file']['type']` and validates against a whitelist of allowed types.  **Crucially important** to prevent users from uploading executable files disguised as other types.
        * **File Size Validation:**  Limits the maximum file size.
        * **Unique Filenames:** Generates unique filenames using `uniqid()` to prevent overwriting files and potential security vulnerabilities.
        * **Move Uploaded File:** Uses `move_uploaded_file()` to securely move the uploaded file.
    * **Session Management:** Uses `session_start()` and stores the user ID in the session after successful login.  This allows you to track logged-in users and restrict access to certain pages.  Include `session_start()` at the very beginning of files that require authentication.

* **Error Handling:** Includes more robust error handling for file uploads and database operations.  Error messages are displayed to the user (though, in a production environment, you'd want to log these errors instead).

* **Functionality:**
    * **Registration:**  Basic user registration functionality.
    * **Login:**  Basic user login functionality with password verification.
    * **File Upload:**  File upload functionality with security checks and storage in a dedicated directory.
    * **Database Storage:** Stores file information (user ID, filename, original name, upload date) in the database.

* **Code Structure:**
    * **Functions:**  Uses functions to encapsulate reusable logic (e.g., `hashPassword`, `verifyPassword`, `generateUniqueFileName`, `uploadFile`).  This makes the code more organized and easier to maintain.
    * **Clearer HTML:**  The HTML form is now more basic, allowing you to easily customize it.  It conditionally shows the registration/login forms or the upload form based on whether the user is logged in.

* **Important Considerations:**
    * **Database Setup:**  You need to create a MySQL database named `secure_file_storage` and two tables: `users` and `files`. Here's the SQL to do that:

    ```sql
    CREATE DATABASE secure_file_storage;

    USE secure_file_storage;

    CREATE TABLE users (
        id INT AUTO_INCREMENT PRIMARY KEY,
        username VARCHAR(255) NOT NULL UNIQUE,
        password VARCHAR(255) NOT NULL
    );

    CREATE TABLE files (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        file_name VARCHAR(255) NOT NULL,
        original_name VARCHAR(255) NOT NULL,
        upload_date DATETIME NOT NULL,
        FOREIGN KEY (user_id) REFERENCES users(id)
    );
    ```

    * **`uploads` Directory:**  Create a directory named `uploads` in the same directory as your PHP script.  Make sure the web server has write permissions to this directory.  On Linux/macOS, you might need to run `chmod 777 uploads` (use with caution in production - see security below).
    * **`logout.php`:** Create a `logout.php` file with the following content:

    ```php
    <?php
    session_start();
    session_unset();
    session_destroy();
    header("Location: index.php"); // Redirect to the main page (e.g., your index.php)
    exit();
    ?>
    ```
    * **`login.php` and `upload.php` :** Create files named `login.php` and `upload.php`. This example assumes that when the user successfully logs in they are redirected to the `upload.php` file. You can include the session check and upload code directly within `upload.php`.

* **Further Security Enhancements (Production):**

    * **More Robust Input Validation:**  Implement more thorough input validation on all user inputs (usernames, passwords, filenames, etc.) to prevent cross-site scripting (XSS) and other vulnerabilities.  Use regular expressions or other validation techniques.
    * **CSRF Protection:** Implement Cross-Site Request Forgery (CSRF) protection to prevent attackers from forging requests on behalf of logged-in users.
    * **HTTPS:**  **Absolutely essential.**  Use HTTPS to encrypt all communication between the client and the server.
    * **Secure File Storage:**
        * **Store files outside the web root:**  Store the uploaded files in a directory that is *not* accessible directly via the web server.  This prevents users from directly accessing the files by URL.  Then, use a PHP script to serve the files with proper authentication and authorization checks.  This is a standard security practice.
        * **Database Encryption:** Consider encrypting sensitive data in the database, such as filenames or file metadata.
    * **Regular Security Audits:** Conduct regular security audits of your code and infrastructure to identify and address potential vulnerabilities.
    * **Error Logging:** Implement robust error logging to track errors and security events. Don't display sensitive error information to the user; log it to a secure location.
    * **Update Dependencies:** Keep your PHP version, MySQL server, and all libraries and frameworks up to date with the latest security patches.
    * **Permissions:** NEVER use `chmod 777`. It opens up security holes. Set the permissions as restrictive as possible while still allowing the web server to function.  Typically, the web server user (e.g., `www-data`, `apache`, `nginx`) needs to be the *owner* of the uploads directory and have read/write access.  On a Linux system, you might do something like:

      ```bash
      chown www-data:www-data uploads
      chmod 755 uploads
      ```

      Replace `www-data` with the actual user your web server runs as.

This revised example provides a much stronger foundation for a secure file storage system.  Remember to implement all the recommended security enhancements before deploying to a production environment.  Security is an ongoing process, so stay vigilant and keep your code and infrastructure up to date.
👁️ Viewed: 9

Comments