I wonder how to publishing private user files but in the way only that user can access it. I mean, after logging in, there would be many files that only the user logged in can access. For instance, an image collection or maybe a mp3 file to play in a html5 player or a pdf to download. But the point is the user logged is the owner of the files and he is the only one who can get them.
My problem is that in the HTML code I need to provide a link in the image attribute or a link in the html5 mp3 player or a file link to download it. And this link has to be pointing to a public directory so it can be accessed by everybody.
My question is: how do people implement that kind of security or functionality nowadays?
Another example. In facebook, you have your own private images and even if you give the full link of any private image to a friend, he cannot see it unless you tag that image as "public" or something like that.
I have been researching about the ideas people have given me here. I did try some things; for instance, I went to Facebook and got the link of a private image of mine (by right clicking over and copy image link...) I put that link in other browser and after logging out of Facebook, and I can see the image in the browser so the conclusion is the file has to be public in the moment we access. Other thing is we hide the name file or something like that.
I propose to:
Users file has to be in a folder which has a "unique reference" as name that only can know the own user. So by storing this reference in a database like a password ... you got the idea...
Sometimes, we will need to have "public files" with limited access, I mean, I want to people to play a mp3 in the html5 player or a video, but I don't want to let them to download it. In this cases, we could obfuscate the code making it hard to find the full link.
Alright. Since I'm unformilliar with your code, I'm going to use a bit of general code as an example. All you have to do is adjust it.
First a very basic html that will upload the video / mp3 / image or whatever:
<form name="upload" action="" method="POST" ENCTYPE="multipart/form-data">
    Select the file to upload: <input type="file" name="userfile">
    <input type="submit" name="upload" value="upload">
</form>
Next you need to prepare your database table:
CREATE TABLE `uploads` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `userid` INT(11) NOT NULL,
    `name` VARCHAR(64) NOT NULL,
    `original_name` VARCHAR(64) NOT NULL,
    `mime_type` VARCHAR(20) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
Now comes the file upload part. At this point I should mention that I'm not very well formilliar with MySQLi. Therefor I'm using PDO in this example. You should however be able to adjust it to MySQLi if you prefer:
<?php
session_start();
# My PDO class. A link to it can be found at the bottom of this answer
require_once 'pdo.class.php';
# Send user back login if not logged
if(!isset($_SESSION['your_login_userid_here'])){
    Header("Location: your_login.php");
}
# If file is uploaded
if(isset($_POST['upload'])){
    $uploaddir = 'uploads'; # Your upload directory
    function tempnam_sfx($path, $suffix){
        do {
            $file = $path."/".mt_rand().$suffix;
            $fp = @fopen($file, 'x');
        }
        while(!$fp);
        fclose($fp);
        return $file;
    }
    # Make a regular expression to check for allowed mime types
    $pattern = "#^(image/|video/|audio/)[^\s\n<]+$#i";
    if(!preg_match($pattern, $_FILES['userfile']['type']){
        die("Only image, video and audio files are allowed!");
    }
    $uploadfile = tempnam_sfx($uploaddir, ".tmp");
    if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
        # Define db configuration
        define("DB_HOST", "localhost");
        define("DB_USER", "username");
        define("DB_PASS", "password");
        define("DB_NAME", "dbname");
        $db = new Database;
        $db->query("INSERT INTO uploads SET userid=:id, name=:filename, original_name=:oriname, mime_type=:mime");
        $db->bind(":userid",$_SESSION['your_login_userid_here']);
        $db->bind(":filename",basename($uploadfile));
        $db->bind(":oriname",basename($_FILES['userfile']['name']));
        $db->bind(":mime",$_FILES['userfile']['type']);
        try {
            $db->execute();
        } catch (PDOException $e) {
            unlink($uploadfile);
            die("Error saving data to the database. The file was not uploaded");
        }
        $id = $db->lastInsertId();
        echo "File succesfully uploaded.\n";
    } else {
        echo "File uploading failed.\n";
    }
} else {
    # No upload received. Send user to upload page
    Header("Location: html_upload_form.html");
}
?>
So what is happening? Basicly we are uploading the file to our upload dir where we give it a random filename with the .tmp extension. In our database we're saving this random filename, the original filename, and what type of file it is. Ofcourse we're adding the userid as well so we know to whom to file belongs. The benefits of this approach are as follows:
Up comes the PHP file that will retrieve the uploaded file for us:
<?php
session_start();
require_once 'pdo.class.php';
# Send user back login if not logged
if(!isset($_SESSION['your_login_session_here'])){
    Header("Location: your_login.php");
}
# Define db configuration
define("DB_HOST", "localhost");
define("DB_USER", "username");
define("DB_PASS", "password");
define("DB_NAME", "dbname");
$uploaddir = 'uploads/';
$id = $_GET['id'];
if(!is_numeric($id)) {
   die("File id must be numeric");
}
$db = new Database;
$db->query('SELECT userid, name, mime_type FROM uploads WHERE id=:id');
$db->bind(":id", $id);
try {
    $file = $db->single();
} catch (PDOException $e) {
    die("Error fetching data from the database");
}
# Check if file belongs to user
if($_SESSION['your_login_session_here'] != $file['userid']){
    die("This file does not belong to you!");
}
if(is_null($file) || count($file)==0) {
    die("File not found");
}
$newfile = $file['original_name']; # The original filename
# Send headers and file back
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename='.basename($newfile));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($uploaddir.$file['name']));
header("Content-Type: " . $file['mime_type']);
readfile($uploaddir.$file['name']);
?>
What is happening? In this file, you're using the file id to retrieve the users file from the database. That way no user needs to know any filename at all! Thanks to our headers, the owner of the file will however be able to download the file with it's original name, without knowing the filename on the server at all.
So next I will give you a short example on how to show the user all his files:
<?php
session_start();
require_once 'pdo.class.php';
# Send user back login if not logged
if(!isset($_SESSION['your_login_session_here'])){
    Header("Location: your_login.php");
}
# Define db configuration
define("DB_HOST", "localhost");
define("DB_USER", "username");
define("DB_PASS", "password");
define("DB_NAME", "dbname");
$db = new Database;
# Retrieve all files from the user and build links
$db->query("SELECT id, original_name, mime_type FROM uploads WHERE userid=:id");
$db->bind(":id",$_SESSION['your_login_session_here']);
try {
    $files = $db->resultset();
} catch (PDOException $e) {
    die("Error fetching data from the database");
}
if($db->rowCount() > 0){
    foreach($files as $file){
        echo "<a href='your_html_viewer_file.php?id=". $file['id'] "&type=". $file['mime_type'] .">". $file['original_name'] ."</a><br />";
    }
} else {
    echo "No files found!";
}
?>
And finally comes the PHP file that will display the file in some HTML mediaplayer. I will only 2 examples here, you should be able add your own very easily:
<?php
session_start();
# Send user back login if not logged
if(!isset($_SESSION['your_login_session_here'])){
    Header("Location: your_login.php");
}
$id = $_GET['id'];
$type = $_GET['type'];
if(strpos($type, 'video/') === 0){ ?>
    <video width="480" height="320" controls>
        <source src="your_file_retriever.php?id=<?php echo $id; ?>" type="<?php echo $type; ?>">
    </video>
<?php } else if(strpos($type, 'audio/') === 0){ ?>
    <audio controls>
        <source src="your_file_retriever.php?id=<?php echo $id; ?>" type="<?php echo $type; ?>">
    </audio>
<?php } ?>
Now to make sure no one is going to just brute force attack your uploads folder, you need to create a .htaccess file inside this folder. The following code will block anyone from accessing that folder except the server itself ofcourse:
order deny,allow
deny from all
allow from 127.0.0.1
My PDO Class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With