【问题标题】:Getting private files or showing private images in a HTML page在 HTML 页面中获取私有文件或显示私有图像
【发布时间】:2015-09-05 07:45:17
【问题描述】:

我想知道如何发布私人用户文件,但只有该用户可以访问它。我的意思是,登录后,会有很多文件只有登录的用户才能访问。例如,要在 html5 播放器中播放的图像集或 mp3 文件或要下载的 pdf 文件。但关键是登录的用户是文件的所有者,他是唯一可以得到它们的人。

我的问题是,在 HTML 代码中,我需要提供图像属性中的链接或 html5 mp3 播放器中的链接或文件链接来下载它。而且这个链接必须指向一个公共目录,这样每个人都可以访问它。

我的问题是:如今人们如何实现这种安全性或功能?

另一个例子。在 facebook 中,您有自己的私人图像,即使您将任何私人图像的完整链接提供给朋友,除非您将该图像标记为“公开”或类似的东西,否则他无法看到它。

可能的解决方案

我一直在研究人们在这里给我的想法。我确实尝试了一些东西;例如,我去 Facebook 并获得了我的私人图像的链接(通过右键单击并复制图像链接......)我将该链接放在其他浏览器中,并在退出 Facebook 后,我可以看到图像在浏览器中,所以结论是文件必须在我们访问的那一刻公开。另一件事是我们隐藏了名称文件或类似的东西。

我建议:

  • 用户文件必须位于具有“唯一引用”作为名称的文件夹中,该名称只能知道自己的用户。因此,通过将此引用存储在数据库中,如密码……你明白了……

  • 有时,我们需要访问受限的“公共文件”,我的意思是,我希望人们在 html5 播放器中播放 mp3 或视频,但我不想让他们下载它。在这种情况下,我们可能会混淆代码,从而难以找到完整的链接。

【问题讨论】:

  • 恐怕这是一个非常广泛的主题,无法在此涵盖。有很多很多方法可以实现这一点。最好在服务器端进行,因为访问者总是可以修改 JavaScript。
  • 我发现最安全的选择是将用户文件保存在文档根目录之外。但是,要做到这一点,您需要完全访问您的服务器。默认情况下,php 不能遍历文档根目录之外。所以你需要做一些调整来允许这个。完成后,根本没有任何直接链接到可以工作的文件。然而,PHP 本身可以访问它们并将它们返回给用户。
  • 非常感谢 Darren H,你知道任何资源或教程涵盖这个问题吗?我找不到谷歌搜索:-(
  • 嗨,icecub,谢谢:您的意思是请求包含文件本身,而不是提供指向“公共文件”的链接?所以,我喜欢这样,但我怎么能告诉 htm5 加载例如和图像或在 html5 播放器中播放 mp3 文件?因为据我所知,它需要一个资源链接!!
  • 我花了一点时间才找到它,但我想你会发现这个 PDF 非常有趣,可以满足你的需要:Secure file upload in PHP web applications 它也涵盖了阅读它们。

标签: javascript html


【解决方案1】:

好的。由于我对您的代码不熟悉,因此我将使用一些通用代码作为示例。你所要做的就是调整它。

首先是一个非常基本的 html,它将上传视频/mp3/图像或其他内容:

<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>

接下来你需要准备你的数据库表:

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;

现在是文件上传部分。在这一点上,我应该提到我对 MySQLi 不是很熟悉。因此我在这个例子中使用了 PDO。但是,如果您愿意,您应该能够将其调整为 MySQLi:

<?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");
}

?>

那么发生了什么?基本上我们将文件上传到我们的上传目录,在那里我们给它一个带有.tmp扩展名的随机文件名。在我们的数据库中,我们保存了这个随机文件名、原始文件名以及它是什么类型的文件。当然,我们也添加了用户 ID,以便我们知道文件属于谁。这种方法的好处如下:

  • - 没有人会知道服务器上的文件名。
  • - 除了所有者之外,没有人会知道原始文件名。
  • - Mime 类型允许我们设置 HTML5 媒体播放器。
  • - 所有者可以下载文件,但其他人无法下载。

来了 PHP 文件,它将为我们检索上传的文件:

<?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']);

?>

发生了什么?在此文件中,您使用文件 ID 从数据库中检索用户文件。这样,用户根本不需要知道任何文件名!多亏了我们的标头,文件的所有者将能够使用它的原始名称下载文件,而根本不知道服务器上的文件名。

接下来我将给你一个简短的例子,说明如何向用户显示他的所有文件:

<?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!";
}

?>

最后是 PHP 文件,它将在一些 HTML 媒体播放器中显示该文件。我这里只举两个例子,你应该可以很容易地添加你自己的:

<?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 } ?>

现在要确保没有人会暴力攻击您的上传文件夹,您需要在此文件夹中创建一个 .htaccess 文件。以下代码将阻止任何人访问该文件夹,当然除了服务器本身:

order deny,allow
deny from all
allow from 127.0.0.1

我的PDO Class。

【讨论】:

  • @user2992476 感谢您的赞赏。如果出现任何问题,请告诉我,我会尽力解决。
【解决方案2】:

有很多解决方案。我用于约会网站的文件夹名称与用户的唯一参考相同。那里的所有文件都属于该用户。但是访问仍然由 php 控制,在 html 中提供正确的链接。

这是简化的答案。因为我不希望图像文件夹中的子文件夹数量达到数千个,所以我将它们散列到另一个级别的文件夹中。

【讨论】:

  • 非常感谢 Rohit Gupta,所以,在您的解决方案中,关键是只有用户所有者知道唯一引用(文件夹名称),所以文件仍然是公开的,唯一引用就像“私钥”不是吗?
  • 在我的使用中,文件都是只读的,任何人都可以看到它们。如果用户想要编辑或替换它们,那么这会通过一个新的临时随机命名文件夹来实现。在您的情况下,以上回答了存储问题。你仍然需要整理出适合的安全性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-25
  • 1970-01-01
  • 2017-08-08
  • 1970-01-01
  • 2012-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多