以性能为导向的方法来保护PHP级别的文件?

Performance-oriented way to protect files on PHP level?

提问人:Pekka 提问时间:11/7/2009 最后编辑:GEOCHETPekka 更新时间:11/30/2017 访问量:2481

问:

我正在寻找一些关于我一直在思考的事情的意见。这是一个非常普遍的问题,也许有我还没有想到的解决方案。

我有一个基于PHP的CMS。
对于在CMS中创建的每个页面,用户可以上传资产(要下载的文件,图像等)

这些资源存储在一个目录中,我们称之为“/myproject/assets”,每页(1 个子目录 = 1 个页面,例如“/myproject/assets/page19283”)

用户可以在 CMS 中“取消发布”(隐藏)页面。当一个页面被隐藏时,有人试图访问它,因为他们记住了 URL 或者他们来自 Google 或其他东西,他们会收到一条“未找到”消息。

但是,这些资产仍然可用。我也想保护它们,这样当用户取消发布页面时,他们可以相信它已经完全消失了。(对于司法问题非常重要,例如法院命令删除内容......这样的事情可能会发生)。

最明显的方法是将所有资产存储在一个安全的目录中(= Web 服务器无法访问),并使用 PHP“前门”在检查后将文件传递出去。当一个项目需要无懈可击时,这是我目前的做法,但我不喜欢它,因为 PHP 解释器会为网站上的每个微小图像、脚本和样式表运行。我想要一个更快的方法。

.htaccess保护(拒绝所有或类似)并不完美,因为CMS应该是可移植的,并且能够在共享环境中运行。我希望它甚至可以在 IIS 和其他 Web 服务器上运行。

我现在能想到的最好的方法是在未发布时将特定页面的资产目录移动到安全位置,并在发布时将其移回。但是,管理员用户需要能够看到该页面,即使该页面未发布,因此我必须解决必须从安全目录提供这些资产的事实。

谁能想出一种方法,允许Apache直接访问文件(=不通过PHP脚本),但仍然使用PHP控制访问?我不能。

我还会考虑一个简单的 .htaccess 解决方案,它可能会在大多数共享环境中运行。

php 安全 apache .htaccess

评论


答:

1赞 Kevin Peno 11/7/2009 #1

编辑:管理界面的混合怎么样?在 ACP 中,您可以通过 PHP 方法访问,基本上,将所有文件请求发送到 PHP 身份验证文件,但对于公共文件,您可以使用 HTTP AUTH/htaccess 来确定结果的可用性。这为您提供了公共端的性能,但提供了 ACP 端的保护。

旧消息:

使用mod_rewrite类型操作时,.htaccess 与大多数 Apache 和 IIS<7 环境(使用各种 ISAPI 模块)兼容。唯一的例外是 IIS7 + 使用 web.config 文件的新重写模块。但是,我愿意您可以有效地为此实例生成/更改 web.config 文件,而不是使用 .htaccess。

鉴于此,您可以使用 rewrite 方法设置重定向并重定向到您的自定义 404 页面(希望发送正确的 404 标头)。这不是 100% 合适的,因为实际资产应该是提供 403 标头的资产,但是......它有效。

除非您想为每个服务器平台正确创建HTTP AUTH设置,否则我会走这条路线。另外,如果你做对了,你可以使你的系统可扩展,以允许你或你的用户将来使用其他类型(包括一个基于php的选项,如果他们想这样做的话)。

评论

0赞 Pekka 11/7/2009
我忘了在我的问题中提到,阻止对htaccess级别的资产的访问有一个额外的问题,即该页面应该仍然对在CMS中工作的用户可见。不过,很高兴知道 IIS 具有类似 Apache 的每目录命令,至少其中一些命令。+1
0赞 Kevin Peno 11/7/2009
呸,真是咄咄逼人。我知道那是你的角度。对不起,我有一个漫长的早晨。
0赞 Pekka 11/7/2009
混合方法有点工作非常好,因为它确实结合了两全其美。我可能会接受。
2赞 cgr 11/7/2009 #2

任何敏感的东西都应该像你建议的那样存放在安全的区域。

如果您的网站位于 /var/www/public_html

将资产放在 /var/www/assets 中的 Web 可访问区域之外 PHP可以调用下载,或者您可以根据需要通过PHP提供文件。

如果您将 HTML 保留在 CMS 数据库中,则只会留下非敏感图像和 CSS。

如果您绝对必须打开和关闭对所有材料的所有访问,我认为您最好的选择可能是符号链接。将 -everything- 保留在无法访问 Web 的区域中,并将每个资产文件夹符号链接到 Web 区域。这样,如果您需要完全锁定人员,只需删除符号链接而不是删除所有文件即可。

我不喜欢它,但这是我能想到的唯一适合你的东西。

评论

0赞 Pekka 11/7/2009
这些资产本身并不敏感,只有当它们属于未发布时。符号链接是我还没想到的好主意,但遗憾的是,我不能依赖共享环境中的符号链接。
0赞 Pekka 11/7/2009
-- 本来是上面的“未发布的页面”。和 +1
0赞 metrobalderas 11/7/2009 #3

我会去实现一个网关。您将 .htaccess 文件设置为指向网关 .php 脚本的 /assets/ URL,如果两个凭据都无效并且此特定文件未发布或显示它,则该脚本将拒绝。

我有点困惑。您是否还需要保护样式表文件和图像?也许移动此文件夹是最好的选择。

1赞 Scott Saunders 11/7/2009 #4

我假设“页面”是由PHP生成的,而“资产”不应该需要PHP。(如果我弄错了,请告诉我。

您可以重命名 assets 文件夹。例如,将“/myproject/assets/page19283”重命名为“/myproject/assets/page19283-hidden”。这将破坏所有旧的、记忆的链接。当您为可以看到该页面的管理员用户生成页面时,只需使用新文件夹名称编写 URL。毕竟,您知道该页面是否“隐藏”。如果您知道“秘密”网址,则可以直接访问资产。

为了提高安全性,请使用一堆随机文本重命名文件夹,并将其存储在页表中(存储隐藏标志的任何位置):“/myproject/assets/page19283-78dbf76B&76daz1920bfisd6g&dsag”。这将使猜测隐藏的 url 变得更加困难。

评论

1赞 Pekka 11/7/2009
好主意,但不能提供 100% 的安全性。指向隐藏资源的链接可以通过管理员用户的浏览器缓存找到自己的方式。这些文件需要完全无法触及。
1赞 Pickle 11/7/2009 #5

只需将 GUID 预置或追加到数据库中的页面名称和文件系统中的资源目录中即可。管理员仍然可以从管理界面查看它,因为链接将更新,但 GUID 有效地使外部用户或搜索引擎无法发现该页面。

评论

0赞 Kevin Peno 11/7/2009
+1 的想法,但它可能也必须绑定到 .htaccess 文件,以防止由 guid 引起的丑陋链接。
2赞 ntd 11/7/2009 #6

我只是阻止任何非HTML文件的热链接,因此所有“资产”内容只能从HTML页面访问。删除(或保护)页面只是删除所有内容,而不必弄乱整个文件系统。

评论

0赞 Pekka 11/7/2009
非常非常有趣的想法,它不是 100% 安全的,因为可以伪造推荐人。嗯......
1赞 mattbasta 11/7/2009 #7

将您的信息存储在 Web 根目录之外的目录中(即:public_html 或 htdocs 之外的一个目录)。然后,在 php 脚本中使用 readfile 运算符在请求时代理文件。readfile(...) 基本上采用单个参数(文件的路径)并打印该文件的内容。

这样,您可以创建一个屏障,如果访问者请求隐藏在代理后面的信息,即使他们“记住”了 URL,您也可以使用 404 或 403 拒绝他们。

评论

0赞 Pekka 11/7/2009
是的,正如我所写的,这就是我目前的做法。不过,我希望避免 readfile() 瓶颈,因为它适用于正在调用的每个微小资源。
0赞 mattbasta 11/7/2009
然后,也许混合方法就应该有序了:“受保护”资产文件夹和“代理”文件夹。这实际上是在没有 htaccess 的情况下提供安全性的唯一方法(您可以拥有安全、快速或便携。
2赞 kojow7 11/30/2017 #8

使用 X-Sendfile

最好和最有效的方法是使用 X-Sendfile。但是,在使用 X-Sendfile 之前,您需要在 Web 服务器上安装和配置它。

如何执行此操作的方法将取决于您使用的 Web 服务器,因此请查找特定服务器的说明。实施起来应该只有几个步骤。实施后,不要忘记重新启动 Web 服务器。

安装 X-Sendfile 后,您的 PHP 脚本只需检查登录用户,然后提供该文件。下面可以看到一个使用会话的非常简单的例子:

session_start();

if (empty($_SESSION['user_id'])){
    exit;
}


$file = "/path/to/secret/file.zip";
$download_name = basename($file);

header("X-Sendfile: $file");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . $download_name . '"');

重要提示:

如果您想从另一个网页(例如图像 src 值)提供文件,则需要确保清理文件名。您不希望任何人覆盖您的脚本并使用“..”等来访问您系统上的任何文件。

因此,如果您有如下所示的代码:

<img src="myscript.php?file=myfile.jpg">

然后你会想做这样的事情:

session_start();

if (empty($_SESSION['user_id'])){
    exit;
}

$file = preg_replace('/[^-a-zA-Z0-9_\.]/', '', $_GET['file']);
$download_name = basename($file);

header("X-Sendfile: $file");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . $download_name . '"');