1

正如标题所说,我制作了一个脚本来读取 pdf 文件。只能打开特定文件。可以打开最后修改到 2008 年 9 月 29 日的所有文件。之后的所有文件都不能。

这是我的代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Stienser Omroeper</title>
</head>

<body>

<?php
$file = 'E:/Omrop/'.$_GET['y'].'/'.$_GET['f'];
$filename = $_GET['f'];

header('Content-type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($file));
header('Accept-Ranges: bytes');
@readfile($file);
?>
</body>
</html>

$_GET 包含 y(地图结构的年份)和 f(文件名)。如果我在我的电脑上回显 $file 并使用运行中的链接,它会完美运行。在浏览器中,我收到消息此文件已损坏,无法修复..

任何人的想法?

4

1 回答 1

1

此代码包含文件系统遍历漏洞。您没有对导致该文件的参数执行任何验证。磁盘上的文件被盲目打开并提供给客户端。

如果你在 Unix 系统上怎么办?如果有人提交会发生什么?y=&f=../../../etc/passwd

这甚至没有触及您没有对用户所需的文件名进行任何清理的事实。用户可以在那里提交完全伪造的数据并获得完全伪造的文件名。

此代码不执行错误检查,甚至在使用readfile. 这是你问题的根源。没有人知道出了什么问题。

所以,我们可以解决这个问题。

首先,您需要对yand进行一些验证f。你提到那y是一年,所以

$year = (int)$_GET['y'];

应该做的伎俩。通过将其强制为整数,您可以消除那里的任何可怕之处。

f会有点棘手。您还没有告诉我们文件的名称。您将需要添加一些模式匹配验证,以确保只查找有效的文件名。例如,如果所有 PDF 都被命名为“report_something_0000.pdf”,那么您需要进行验证,比如说

$file = null;
if(preg_match('/^report_something_\d{4}\.pdf$/', $_GET['f'])) {
    $file = $_GET['f'];
}

现在我们已经有了一个有效的文件名和一个有效的年份目录,下一步是确保文件存在

$path = 'E:/Omrop/' . $year . '/' . $file;
if(!$file || !file_exists($path) || !is_readable($path)) {
    header('HTTP/1.0 404 File Not Found', true, 404);
    header('Content-type: text/html');
    echo "<h1>404 File Not Found</h1>";
    exit;
}

如果$file由于模式匹配失败而最终没有设置,或者如果未找到生成的文件路径,则脚本将退出并显示错误消息。

猜测您打开旧 PDF 的问题是由文件不存在或权限错误引起的。您正在为 Adob​​e Reader 提供正确的标题,然后没有数据。

您还需要对用户提供的所需文件名执行相同类型的完整性检查。再说一次,我不知道你的要求,但要确保没有任何虚假可以偷偷溜进来。

接下来,去掉前面的@ readfile。它抑制了任何实际的错误,你会想看到它们。因为您可能不想在输出中看到它们,所以请确保设置错误日志

最后......这段代码是如何工作的? 您在 HTML 中间发出标头! 不仅如此,您在这样做时还给出了明确的内容长度。你应该从中得到很多错误。你确定你没有在这里不小心复制/粘贴错误的代码吗?也许您忘记了您正在调用的顶部的部分ob_start()?无论如何,放弃开始标签之前的所有内容。<?php

于 2012-01-17T00:28:49.860 回答