4

我目前正在为一个基本上涉及销售各种文件的客户创建一个网站。这显然是一件很常见的事情,这让我觉得有点愚蠢,因为我没有想到一种方法来做这件事。

购买完成后,客户应被带到包含下载链接的页面,并接收包含下载链接的电子邮件和包含将为他们创建的帐户信息的电子邮件(他们也将能够从他们帐户的控制面板下载)。我想弄清楚的是如何隐藏/隐藏文件在我的服务器上的位置,以便购买它的人不能简单地将直接链接复制并粘贴到其他地方的文件。即使我请求下载文件的链接格式为http://example.com/blah/download/454643,一个与文件实际位置不对应的URL,我认为仍然可以在服务器上找到该文件?我不太了解权限如何在我的服务器上工作,这就是我问的原因。提前致谢 :)

4

8 回答 8

8

您基本上不会向用户提供文件的直接 URL。基于服务器的权限在这里无关。

假设您已将所需文件保存在 /data/files/file.pdf 中(将文件存储在 Web 根目录之外的良好做法)。您可以为用户提供一个类似于 /download.php?auth=32 的下载链接

当用户单击链接时,download.php 将检查会话/cookie 是否经过身份验证以及下载 ID 是否有效(如果您有基于时间的下载到期)然后 download.php 将从其位置读取所需文件并发送将其发送到带有适当标题的浏览器以强制下载。

于 2008-10-16T23:40:38.487 回答
5

将文件存储在您的 Web 根目录之外,但请确保您存储它们的文件夹位于 php.ini 文件的“open_basedir”指令中,这将允许您从 PHP 脚本访问它们。将它们存储在 Web 根目录之外意味着它们永远不会通过 HTTP 直接访问。

有一个 PHP 脚本,就像这些答案中列出的可以流式传输/读出文件的脚本一样。如果它是一个大文件,您可能需要更改“max_execution_time”以考虑脚本读取文件所需的额外时间。此脚本将允许您对用户进行身份验证并检查他们是否已为文件付费。

将 .htacces 放入包含脚本的文件夹中,该脚本将从该文件夹请求的文件重写为变量。这使得他们看起来好像是在直接访问该文件,而实际上并非如此。就个人而言,我只会将单个脚本放在此文件夹中,以保持简单。所以:

http://www.yourdomain.com/files/expensive_song.mp3

实际上重写为:

http://www.yourdomain.com/files/download_file.php?filename=expensive_song.mp3

祝你好运。

于 2008-10-17T07:37:10.100 回答
2

如果您有权运行 Lighttpd,则绝对应该查看Mod_SecDownload模块。我在以前的安全销售视频和图像文件的项目中使用了它。

由于网络服务器对应用程序中使用的权限一无所知,因此每个知道该 URL 的用户都可以使用生成的 URL。

mod_secdownload 通过引入一种在指定时间内对 URL 进行身份验证的方法来消除此问题。应用程序必须生成一个令牌和一个时间戳,这些令牌和时间戳在允许网络服务器下载文件之前由网络服务器检查。

生成的 URL 必须具有以下格式:

<uri-prefix>/<token>/<timestamp-in-hex>/<​​rel-path> 看起来像“yourserver.com/bf32df9cdb54894b22e09d0ed87326fc/435cc8cc/secure.tar.gz”

<token> 是一个 MD5

  1. 一个秘密字符串(用户提供)
  2. (以。。开始 /)
  3. <十六进制时间戳>

如您所见,令牌根本没有绑定到用户。唯一的限制因素是用于在给定超时 (secdownload.timeout) 后使 URL 无效的时间戳。

于 2008-10-17T01:32:21.773 回答
1

一些网络服务器,例如 Lighty 和 Nginx,实现了 X-Sendfile 标头。假设您有一个 Django 应用程序,您可以让您的视图返回一个 X-Sendfile 标头,该标头指向您要发送的文件。然后 lighttpd 将改为提供该文件。

该文件可以位于 Web 不可访问的位置(这不是 301 重定向),并且由于您的应用正在提供标头,因此您可以先进行授权。

这比从您的应用程序中提供静态文件要好得多。网络服务器针对静态文件进行了优化,速度更快,资源更轻。如果您要处理多个请求,则应考虑使用 X-Sendfile。

这里有一篇很好的博客文章:

http://blog.zacharyvoase.com/2009/09/08/sendfile/

Lighttpd/PHP 指令可以在这里找到:

http://redmine.lighttpd.net/wiki/1/X-LIGHTTPD-send-file

NGINX 指令可以在这里找到:

http://wiki.nginx.org/XSendfile

似乎还有一个早期发布的 Apache mod 做同样的事情:

https://tn123.org/mod_xsendfile/

于 2011-11-18T09:53:07.833 回答
1

您可以将 URL 作为买方的授权代码。您让她再次登录,检查代码用于哪个文件,然后将文件通过管道发送给她。这是来自 osCommerce 的 PHP 代码示例(我很久以前写过)。

// Now send the file with header() magic
  header("Expires: Mon, 26 Nov 1962 00:00:00 GMT");
  header("Last-Modified: " . gmdate("D,d M Y H:i:s") . " GMT");
  header("Cache-Control: no-cache, must-revalidate");
  header("Pragma: no-cache");
  header("Content-Type: Application/octet-stream");
  header("Content-disposition: attachment; filename=" . $downloads['orders_products_filename']);

  if (DOWNLOAD_BY_REDIRECT == 'true') {
// This will work only on Unix/Linux hosts
    tep_unlink_temp_dir(DIR_FS_DOWNLOAD_PUBLIC);
    $tempdir = tep_random_name();
    umask(0000);
    mkdir(DIR_FS_DOWNLOAD_PUBLIC . $tempdir, 0777);
    symlink(DIR_FS_DOWNLOAD . $downloads['orders_products_filename'], DIR_FS_DOWNLOAD_PUBLIC . $tempdir . '/' . $downloads['orders_products_filename']);
    if (file_exists(DIR_FS_DOWNLOAD_PUBLIC . $tempdir . '/' . $downloads['orders_products_filename'])) {
      tep_redirect(tep_href_link(DIR_WS_DOWNLOAD_PUBLIC . $tempdir . '/' . $downloads['orders_products_filename']));
    }
  }
于 2008-10-16T23:43:28.637 回答
1

这是我为非常相似的事情所做的示例代码:

// $mimeType is the mime type of the file
header('Content-type: ' . $mimeType);
// this will get the size of the file
// (helps for giving the size to the browser so a percent complete can be shown)
header('Content-length: ' . (string) (filesize($path)));
// disposition is either attachment (for binary files that can't be read by the browser) 
// or inline (for files that can be read by the browser
// some times you have play with this to get working so users get the download window in all browsers
// original filename is the name you want to users to see 
// (shouldn't have any special characters as you can end up with weird issues)
header('Content-Disposition: ' . $disposition . '; filename=' . $originalFilename);
// the next 2 lines try to help the browser understand that the file can't be cached
// and should be downloaded (not viewed)
header('Pragma: Public');
header('Cache-control: private');
// this will output the file to browser
readfile($path);

您当然可以添加任何登录检查和日志记录,以确保它不会被下载太多次。

此外,如前所述,确保将文件放在 Web 服务器文档根目录之外(或之上),这样人们就无法找出路径。或者您甚至可以在目录上设置密码,这样只有内部人员才能更轻松地访问文件列表,但不推荐这样做。(仅当您可以将某些内容放在 doc 根目录之外时才执行此操作。)

于 2008-10-17T02:09:50.620 回答
0

我见过的很多基于购买的下载 url 倾向于使用一些 guid 和其他动态信息作为 url 的一部分,而不是像猜测一个 id 那样简单。您最终可能会使用 guid/datetimepurchased/id 或类似的东西作为路径的一部分。

另一个选项是确保用户在允许下载继续之前登录,这将提供额外的安全层。

于 2008-10-16T23:38:08.493 回答
0

好吧,首先,您绝对不想直接链接到该文件。如果满足某些条件,您可能希望使用生成的 id 参数向用户发送指向您创建的服务(甚至只是一个页面)的链接,该参数会导致文件下载。

确实,这些标准很困难,因为您需要允许用户多次下载文件(以防他第一次下载完整文件失败,或意外删除文件等),但一旦链接有效,它一直有效,直到你杀死它。

我建议使用时间或 IP 来过滤您的下载请求。

时间:当有人从您那里购买文件时,通知他们他们只能下载 1 天的文件,或类似的时间。是的,其他人可以在当天下载该文件,但只能下载 1 天。您也可以对此设置下载限制,因此他们只能下载5次(这是正常的)。

IP:当有人从您那里购买文件时,通知他们他们只能从该 IP 下载文件。当他们尝试下载文件时,您的下载服务当然可以检查这一点。

两者似乎也很容易同时使用。

在任何一种情况下(或两者兼有),准备处理没有及时下载文件的客户,或者想在时间限制后再次获取(或从另一台计算机/IP(有些人没有得到静态的)) )。他们不想再付钱,而且可能不应该付钱。

于 2008-10-16T23:39:16.603 回答