Secure File Sharing System PHP, MySQL
👤 Sharing: AI
```php
<?php
// Database Configuration
define('DB_HOST', 'localhost');
define('DB_USER', 'your_db_user');
define('DB_PASS', 'your_db_password');
define('DB_NAME', 'file_sharing_db');
// File Upload Directory (relative to this script's location)
define('UPLOAD_DIR', 'uploads/');
// Security: Salt for password hashing
define('SALT', 'your_random_salt_string'); // Change this to a strong, random value
// Session Configuration
session_start();
// Helper Functions
/**
* Connects to the database.
*
* @return mysqli|false Database connection object or false on failure.
*/
function connect_db() {
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
error_log("Database connection failed: " . $conn->connect_error); // Log error for debugging
return false;
}
return $conn;
}
/**
* Sanitizes user input to prevent SQL injection.
*
* @param string $input The input string to sanitize.
* @param mysqli $conn The database connection object.
* @return string The sanitized string.
*/
function sanitize_input($input, $conn) {
return $conn->real_escape_string(trim($input));
}
/**
* Hashes a password using a salt.
*
* @param string $password The password to hash.
* @return string The hashed password.
*/
function hash_password($password) {
return password_hash(SALT . $password, PASSWORD_DEFAULT); // Use password_hash for better security
}
/**
* Verifies a password against a stored hash.
*
* @param string $password The password to verify.
* @param string $hash The stored password hash.
* @return bool True if the password matches the hash, false otherwise.
*/
function verify_password($password, $hash) {
return password_verify(SALT . $password, $hash); // Use password_verify for better security
}
/**
* Generates a unique filename to prevent overwrites.
*
* @param string $original_filename The original filename.
* @return string A unique filename.
*/
function generate_unique_filename($original_filename) {
$extension = pathinfo($original_filename, PATHINFO_EXTENSION);
$filename = pathinfo($original_filename, PATHINFO_FILENAME);
return uniqid($filename . '_', true) . '.' . $extension;
}
/**
* Checks if the user is logged in.
*
* @return bool True if the user is logged in, false otherwise.
*/
function is_logged_in() {
return isset($_SESSION['user_id']);
}
/**
* Redirects to the specified URL.
*
* @param string $url The URL to redirect to.
*/
function redirect($url) {
header("Location: " . $url);
exit;
}
/**
* Displays an error message.
*
* @param string $message The error message to display.
*/
function display_error($message) {
echo '<div class="error">' . htmlspecialchars($message) . '</div>'; // Escape message for safety
}
/**
* Displays a success message.
*
* @param string $message The success message to display.
*/
function display_success($message) {
echo '<div class="success">' . htmlspecialchars($message) . '</div>'; // Escape message for safety
}
// ************************************************************
// Registration Handling
// ************************************************************
if (isset($_POST['register'])) {
$conn = connect_db();
if (!$conn) {
die("Database connection failed.");
}
$username = sanitize_input($_POST['username'], $conn);
$password = $_POST['password']; // No need to sanitize for hashing
$confirm_password = $_POST['confirm_password'];
if (empty($username) || empty($password) || empty($confirm_password)) {
display_error("All fields are required.");
} elseif ($password !== $confirm_password) {
display_error("Passwords do not match.");
} else {
// Check if username already exists
$check_sql = "SELECT id FROM users WHERE username = ?";
$check_stmt = $conn->prepare($check_sql);
$check_stmt->bind_param("s", $username);
$check_stmt->execute();
$check_stmt->store_result();
if ($check_stmt->num_rows > 0) {
display_error("Username already exists. Please choose a different one.");
} else {
// Hash the password
$hashed_password = hash_password($password);
// Insert user into database
$insert_sql = "INSERT INTO users (username, password) VALUES (?, ?)";
$insert_stmt = $conn->prepare($insert_sql);
$insert_stmt->bind_param("ss", $username, $hashed_password);
if ($insert_stmt->execute()) {
display_success("Registration successful! Please log in.");
} else {
display_error("Registration failed: " . $conn->error);
error_log("Registration failed: " . $conn->error); // Log the error
}
}
$check_stmt->close();
}
$conn->close();
}
// ************************************************************
// Login Handling
// ************************************************************
if (isset($_POST['login'])) {
$conn = connect_db();
if (!$conn) {
die("Database connection failed.");
}
$username = sanitize_input($_POST['username'], $conn);
$password = $_POST['password'];
if (empty($username) || empty($password)) {
display_error("Username and password are required.");
} else {
// Retrieve user from database
$sql = "SELECT id, username, password FROM users WHERE username = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 1) {
$user = $result->fetch_assoc();
if (verify_password($password, $user['password'])) {
// Set session variables
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
display_success("Login successful!");
redirect("index.php"); // Redirect to the main page after login
} else {
display_error("Incorrect password.");
}
} else {
display_error("User not found.");
}
$stmt->close();
}
$conn->close();
}
// ************************************************************
// Logout Handling
// ************************************************************
if (isset($_GET['logout'])) {
session_unset();
session_destroy();
redirect("login.php");
}
// ************************************************************
// File Upload Handling
// ************************************************************
if (isset($_POST['upload'])) {
if (!is_logged_in()) {
display_error("You must be logged in to upload files.");
exit;
}
$conn = connect_db();
if (!$conn) {
die("Database connection failed.");
}
if ($_FILES['file']['error'] === UPLOAD_ERR_OK) {
$original_filename = basename($_FILES['file']['name']);
$unique_filename = generate_unique_filename($original_filename);
$upload_path = UPLOAD_DIR . $unique_filename;
// Basic security checks (expand this for production)
$file_type = $_FILES['file']['type'];
$file_size = $_FILES['file']['size'];
// Check file type (example: allow only images and PDFs)
$allowed_types = ['image/jpeg', 'image/png', 'application/pdf'];
if (!in_array($file_type, $allowed_types)) {
display_error("Invalid file type. Only JPEG, PNG, and PDF files are allowed.");
$conn->close();
exit;
}
// Check file size (example: limit to 10MB)
$max_file_size = 10 * 1024 * 1024; // 10MB
if ($file_size > $max_file_size) {
display_error("File size exceeds the limit (10MB).");
$conn->close();
exit;
}
if (move_uploaded_file($_FILES['file']['tmp_name'], $upload_path)) {
// Store file information in the database
$user_id = $_SESSION['user_id'];
$sql = "INSERT INTO files (user_id, original_filename, unique_filename, upload_date) VALUES (?, ?, ?, NOW())";
$stmt = $conn->prepare($sql);
$stmt->bind_param("iss", $user_id, $original_filename, $unique_filename);
if ($stmt->execute()) {
display_success("File uploaded successfully!");
} else {
display_error("Failed to store file information: " . $conn->error);
error_log("File storage failed: " . $conn->error); // Log the error
// Consider deleting the uploaded file if database insertion fails
unlink($upload_path);
}
$stmt->close();
} else {
display_error("File upload failed.");
error_log("File upload failed with error code: " . $_FILES['file']['error']);
}
} else {
display_error("File upload error: " . $_FILES['file']['error']);
}
$conn->close();
}
// ************************************************************
// File Download Handling
// ************************************************************
if (isset($_GET['download'])) {
if (!is_logged_in()) {
display_error("You must be logged in to download files.");
exit;
}
$conn = connect_db();
if (!$conn) {
die("Database connection failed.");
}
$file_id = intval($_GET['download']); // Ensure it's an integer
// Retrieve file information from the database
$sql = "SELECT original_filename, unique_filename, user_id FROM files WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $file_id);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 1) {
$file = $result->fetch_assoc();
// Check if the current user is authorized to download the file (e.g., if they uploaded it)
if ($file['user_id'] != $_SESSION['user_id']) {
display_error("You are not authorized to download this file.");
$conn->close();
exit;
}
$file_path = UPLOAD_DIR . $file['unique_filename'];
if (file_exists($file_path)) {
// Set appropriate headers for download
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $file['original_filename'] . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file_path));
ob_clean(); // Clean output buffer
flush();
readfile($file_path);
exit;
} else {
display_error("File not found on the server.");
}
} else {
display_error("File not found in the database.");
}
$stmt->close();
$conn->close();
}
// ************************************************************
// File Deletion Handling
// ************************************************************
if (isset($_GET['delete'])) {
if (!is_logged_in()) {
display_error("You must be logged in to delete files.");
exit;
}
$conn = connect_db();
if (!$conn) {
die("Database connection failed.");
}
$file_id = intval($_GET['delete']);
$sql = "SELECT unique_filename, user_id FROM files WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $file_id);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 1) {
$file = $result->fetch_assoc();
// Check if the current user is authorized to delete the file (e.g., if they uploaded it)
if ($file['user_id'] != $_SESSION['user_id']) {
display_error("You are not authorized to delete this file.");
$conn->close();
exit;
}
$unique_filename = $file['unique_filename'];
$file_path = UPLOAD_DIR . $unique_filename;
// Delete the file from the database first
$delete_sql = "DELETE FROM files WHERE id = ?";
$delete_stmt = $conn->prepare($delete_sql);
$delete_stmt->bind_param("i", $file_id);
if ($delete_stmt->execute()) {
// Now, delete the file from the file system
if (unlink($file_path)) {
display_success("File deleted successfully!");
} else {
display_error("Failed to delete the file from the server.");
error_log("Failed to delete file from server: " . $file_path);
//Consider re-inserting the file's record into the database.
//(This is advanced and requires proper error handling, logging and transactional database operations for reliability)
}
} else {
display_error("Failed to delete file information from the database: " . $conn->error);
error_log("File deletion failed: " . $conn->error); // Log the error
}
$delete_stmt->close();
} else {
display_error("File not found.");
}
$stmt->close();
$conn->close();
redirect("index.php"); // Redirect to the file list page after deletion.
}
// ************************************************************
// HTML Structure and File Listing (example - should be in your templates)
// ************************************************************
// The following code provides a basic HTML structure and file listing.
// You would typically separate this into separate PHP template files
// (e.g., header.php, file_list.php, footer.php) for better organization and maintainability.
?>
<!DOCTYPE html>
<html>
<head>
<title>Secure File Sharing System</title>
<style>
body {
font-family: sans-serif;
}
.error {
color: red;
margin-bottom: 10px;
}
.success {
color: green;
margin-bottom: 10px;
}
.file-list {
margin-top: 20px;
}
.file-list table {
width: 100%;
border-collapse: collapse;
}
.file-list th, .file-list td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.file-list th {
background-color: #f2f2f2;
}
.upload-form {
margin-bottom: 20px;
}
</style>
</head>
<body>
<header>
<h1>Secure File Sharing System</h1>
</header>
<nav>
<?php if (is_logged_in()): ?>
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
<a href="index.php">Home</a> |
<a href="?logout=true">Logout</a>
<?php else: ?>
<a href="login.php">Login</a> |
<a href="register.php">Register</a>
<?php endif; ?>
</nav>
<main>
<?php
// Display error and success messages (if any) - already done in each section.
?>
<?php
if (is_logged_in()): ?>
<section class="upload-form">
<h2>Upload File</h2>
<form action="index.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" required>
<button type="submit" name="upload">Upload</button>
</form>
</section>
<section class="file-list">
<h2>Your Files</h2>
<?php
$conn = connect_db();
if (!$conn) {
die("Database connection failed.");
}
$user_id = $_SESSION['user_id'];
$sql = "SELECT id, original_filename, upload_date FROM files WHERE user_id = ORDER BY upload_date DESC"; // Order by upload date
$stmt = $conn->prepare($sql);
//No need to bind, since only logged in users are shown their respective files.
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo "<table>";
echo "<thead><tr><th>Filename</th><th>Upload Date</th><th>Actions</th></tr></thead>";
echo "<tbody>";
while ($row = $result->fetch_assoc()) {
echo "<tr>";
echo "<td>" . htmlspecialchars($row['original_filename']) . "</td>";
echo "<td>" . htmlspecialchars($row['upload_date']) . "</td>";
echo "<td><a href='?download=" . $row['id'] . "'>Download</a> | <a href='?delete=" . $row['id'] . "' onclick=\"return confirm('Are you sure you want to delete this file?')\">Delete</a></td>"; // Confirm deletion
echo "</tr>";
}
echo "</tbody>";
echo "</table>";
} else {
echo "<p>No files uploaded yet.</p>";
}
$stmt->close();
$conn->close();
?>
</section>
<?php else: ?>
<p>Please <a href="login.php">login</a> or <a href="register.php">register</a> to upload and manage files.</p>
<?php endif; ?>
</main>
<footer>
<p>© <?php echo date("Y"); ?> Secure File Sharing System</p>
</footer>
</body>
</html>
```
**Explanation and Key Improvements:**
1. **Security First:**
* **Prepared Statements:** Uses prepared statements with `bind_param` throughout the code to prevent SQL injection. This is the *most important* security measure.
* **Password Hashing:** Uses `password_hash` and `password_verify` with `PASSWORD_DEFAULT` for strong password hashing. This is far more secure than `md5` or `sha1`. **Never store passwords in plain text.**
* **Salt:** Adds a salt value which is concatenated with the password before hashing for improved password protection. **Crucially, you must change the `SALT` value** to a random, unique string for your application. This value *must* be kept secret.
* **Input Sanitization:** `sanitize_input` function uses `real_escape_string` to escape user input before using it in SQL queries *after* the prepared statement. This acts as a secondary defense. However, if you have properly implemented prepared statements, this function becomes less critical.
* **File Type Validation:** Checks the file type against a whitelist of allowed types *before* uploading. This prevents users from uploading malicious files with disguised extensions.
* **File Size Limit:** Limits the file size to prevent denial-of-service attacks.
* **Unique Filenames:** Generates unique filenames using `uniqid` to prevent overwriting existing files.
* **Authorization:** Checks user IDs against the session user ID to ensure that only authorized users can download or delete files they own.
* **Error Handling:** Uses `error_log` to log errors for debugging purposes. *Never* display sensitive error information to the user in a production environment.
* **Output Escaping:** Uses `htmlspecialchars` when displaying user-provided content (e.g., filenames, messages) to prevent cross-site scripting (XSS) attacks.
2. **Code Structure:**
* **Configuration:** Uses constants for database credentials, upload directory, and salt, making the code more configurable.
* **Helper Functions:** Breaks down the code into reusable helper functions (e.g., `connect_db`, `sanitize_input`, `hash_password`, `redirect`). This improves code readability and maintainability.
* **Clear Separation of Concerns:** Separates the registration, login, logout, file upload, file download, and file deletion logic into distinct sections.
* **Comments:** Includes comments to explain the purpose of each section of the code.
* **Basic HTML Structure:** Provides a basic HTML structure with navigation and file listing. This *should* be separated into template files for a real application.
3. **File Handling:**
* **`move_uploaded_file`:** Uses `move_uploaded_file` to securely move the uploaded file to the upload directory.
* **File Download Headers:** Sets the correct headers for file downloads to ensure that the browser handles the file correctly.
* **File Deletion:** Includes file deletion functionality with confirmation.
4. **Session Management:**
* Uses `session_start()` to start a session.
* Stores the user ID and username in the session.
* Provides logout functionality.
5. **Database:**
* Uses `mysqli` for database interaction. (You could consider PDO for even better database abstraction).
* Includes basic error handling for database connections and queries.
* The SQL schema (table definitions) are provided below.
6. **Important Considerations:**
* **Directory Permissions:** Ensure that the `UPLOAD_DIR` directory is writable by the web server user.
* **.htaccess:** Place a `.htaccess` file in the `UPLOAD_DIR` directory to prevent direct access to the uploaded files. This adds another layer of security. The contents of `.htaccess` should be:
```
deny from all
```
* **Regular Security Audits:** Perform regular security audits of your code and server configuration to identify and fix potential vulnerabilities.
* **Error Handling & Logging:** Robust error handling and logging are critical for debugging and identifying security issues. *Never* display detailed error messages to the user in a production environment.
* **Database Backups:** Regularly back up your database to prevent data loss.
* **HTTPS:** Use HTTPS to encrypt all communication between the client and the server. This is essential for protecting user credentials and sensitive data.
* **Rate Limiting:** Implement rate limiting to prevent brute-force attacks on the login and registration pages.
* **Input Validation:** While sanitation is useful, validation is also important. For example, ensure usernames meet certain length requirements, and passwords meet complexity requirements.
* **Content Security Policy (CSP):** Implement a strong CSP to mitigate XSS attacks.
* **Update Dependencies:** Keep your PHP version, libraries, and other dependencies up to date with the latest security patches.
**SQL Schema:**
```sql
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
registration_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE files (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
original_filename VARCHAR(255) NOT NULL,
unique_filename VARCHAR(255) NOT NULL,
upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
```
**Separate PHP Files for Structure (Recommended):**
For a real application, create separate files for better organization:
* `config.php`: Database configuration, salt, and other settings.
* `functions.php`: Helper functions (e.g., `connect_db`, `sanitize_input`, `hash_password`).
* `header.php`: HTML header, navigation, session start.
* `footer.php`: HTML footer.
* `index.php`: File upload form, file listing.
* `login.php`: Login form and processing.
* `register.php`: Registration form and processing.
Then, in each file, include the necessary files:
```php
<?php
require_once 'config.php';
require_once 'functions.php';
require_once 'header.php'; // or header_login_register.php if login/register page
?>
<!-- Your content here -->
<?php
require_once 'footer.php';
?>
```
This improved code provides a significantly more secure and well-structured foundation for your file sharing system. Remember to adapt the code to your specific requirements and to always prioritize security best practices. Testing the application thoroughly is crucial before deploying it to a production environment.
👁️ Viewed: 11
Comments