7

Uber 简单的例子来说明这一点:

$message = $_POST['message'];

$fp = fopen("log.txt", "a");
fwrite($fp, $message);

fclose($fp);

我应该清理$_POST['message']变量的用户输入吗?

我理解准备好的语句(用于数据库清理)和htmlentities(如果我POST在某个时间将消息输出回屏幕)但在这种情况下,输入只是放在一个日志文件中,该文件将由一个小的 PHP 脚本读取(via fopen()

答案是否取决于阅读方式?例如,如果我确实通过 fopen() 打开了日志文件,它应该是htmlentities,并且如果我打算下载日志文件并使用 Excel 读取它(用于过滤目的),那么没有什么可做的吗?

4

6 回答 6

7

你的代码基本上是无辜的。唯一“明显”的攻击是反复将数据上传到您的服务器,最终耗尽您的磁盘空间。

“消毒”是一种情境。这不是你可以洒在代码上以使其变得更好的东西,就像你可以在食物上加盐一样。也许您会清理 $_POST 数据以防止 SQL 注入攻击,然后在 HTML 上下文中使用这些数据 - 现在您很容易受到 XSS 攻击。也许它是一个图像上传,并且您进行基本的 MIME 类型确定以确保它是一个图像。这一切都很好,很花哨,但是有人上传了儿童色情片,这将通过“它是图像”测试,现在你遇到了一个更大的问题。

由于您正在接受用户数据并将其写入文件,因此无法使用此代码(除了磁盘空间问题)来滥用您的系统。您不能将某些数据序列嵌入到数据中,这会导致 PHP 或底层操作系统突然停止将该数据写入磁盘并开始执行它。上传数据的类型无关紧要,因为它永远不会在可能用于影响脚本执行的上下文中使用。您只是从网络服务器中吸入一些数据,然后将其吐出到磁盘上。您不允许用户影响写入哪个文件(除非您的用户对服务器具有 shell 级别的访问权限,并且可以创建一个名为“log.txt”的符号链接,指向其他更重要的文件)。

真正的问题出现在之后......这个文件写完后你会做什么?如果您以后的代码做了一些愚蠢的事情

include('log.txt');

那么现在你确实有一个问题——你现在已经把这个“无辜”的数据放在磁盘上的一个文件中,并将它变成了潜在的可执行代码。只需<?php exec('rm -rf /') ?>在该文件中的任何地方简单地丢弃您的服务器。

同样,考虑一下类似于 PHP 的固有愚蠢的“安全”措施magic_quotes。PHP 开发人员(错误地愚蠢地)假设从外部世界提交的任何数据都只会在 SQL 上下文中使用,并且无论其最终目的如何,都会对所有数据进行 SQL 转义。更糟糕的是,他们只是假设所有数据库都使用反斜杠作为转义序列。如果您从不使用 MySQL 以外的任何东西,这一切都很好,但如果您使用的是 SQL Server 怎么办?现在您必须将 PHP-provided 翻译Miles O\'BrienMiles O''Brien,本质上必须 UNDO PHP 自动为您所做的。

TL;DR:不要使用霰弹枪“消毒”方法,它们几乎总是无用/毫无意义,只是在之前和之后需要更多的工作。只需在使用数据时使用特定于上下文的方法。

于 2013-08-27T14:29:14.987 回答
3

您应该清理用户输入,但如何处理完全取决于输入的用途。“消毒”是指确保输入对于特定用途是安全或健全的。在您确定用例之前,该术语无法更具体。

您无需担心像 fopen() 这样的 PHP 读/写函数。关注实际解析或分析输入的步骤。一些可能的例子:

  • 如果文件将显示在基本日志阅读器中,您可能需要确保每个输入都限制在一定长度内,并且不包含换行符或您选择的字段分隔符,并且每行的开头是有效时间邮票。
  • 如果文件将显示在 Web 浏览器中,您可能需要确保输入不包含脚本或指向其他资源的链接(如 IMG 标记)。
  • Excel 文件在行长、时间戳和分隔符方面也有类似的问题。只要 Excel 将文件解析为文本,您就不必担心有人包含可执行代码。(此外,现代 Excel 版本会在运行包含宏之前向您发出警告。)
于 2013-01-28T18:12:07.513 回答
1

一般规则是验证输入并清理输出

如果可以以任何方式验证您的输入,那么您应该这样做。如果没有,那么您应该在输出时对其进行清理,以确保它对于所使用的上下文是安全的。

例如,如果您知道无论如何使用message每个字符都应少于 100 个字符,则读取 POST 数据的脚本可以验证并拒绝任何 POST 数据包含超过 100 个字符的输入的请求。

验证是一种“全有或全无”的方法,它拒绝任何不遵循特定规则的东西,而不管输出上下文如何,而清理是根据上下文“使某些东西安全”的过程。我认为做出这种区分很重要。

在您的情况下,您提供的示例代码不会输出(由另一个脚本处理的目的除外)。它更像是一种存储操作而不是输出操作,因为它message可以像文件系统一样容易地写入数据库。在这种情况下,需要锁定的主要攻击面似乎是文件权限,并确保除了您打算执行此操作的脚本和正确的上下文之外,没有任何东西可以读取或写入文件。例如,我意识到您的示例被简化了,但在这种特定情况下,您应该确保将文件写入您的 Web 根目录上方的位置,或者写入适当设置文件夹权限的位置。否则,您可能无意中为网络上的任何人提供了访问http://www.example.com/log.txt的权限如果他们也可以写入它,那么如果他们可以欺骗浏览器以 HTML 格式读取文件,那么就有可能利用某种XSS攻击。旧版本的 Internet Explorer 会尝试检测 MIME 类型,而不是依赖于text/plain(另请参阅此处)的服务器标头值。不过,这些漏洞可能有点离题,我只是提到它们是彻底的,并作为确保文件本身被适当锁定的一个例子。

回到您的问题:在您的情况下,您的验证应该由处理log.txt. 这应该验证文件。请注意,它在此处验证文件,而不是原始message. 该文件应使用其自己的规则进行验证,以确保数据符合预期。如果脚本直接输出任何内容,则应在此处进行清理以匹配输出的上下文。因此,总结一下您的应用程序的验证和清理过程将是:

  1. 创建日志: Web 浏览器 ---POST---> get_message.php---> 验证message是否有效 --- fwrite()-->log.txt

  2. 进程日志: log.txt --- fopen()---> process.php---> 验证文件是否有效 ---> 有什么输出吗?然后在这个阶段进行消毒。

以上假设在脚本进行处理之前进行了正确的授权(即当前用户在您的应用程序中具有登录message或处理日志的权限。)

于 2013-08-27T14:12:44.503 回答
0

我会对其进行消毒。当涉及到日志时,只需确保将其放入保留空间 - 例如,如果日志是每行一条记录,则从用户输入中删除新行和其他内容,这样他就不会欺骗你。

看看名为日志注入的攻击

在显示日志文件时也要非常小心。确保没有输出会伤害您的读者。

于 2013-08-27T16:22:48.183 回答
0
  • 您附加到当前目录中的文件 - 这似乎可以通过浏览器下载,因此您正在创建一个安全漏洞。将文件放在文档根目录之外(最好),或通过 .htaccess 保护它。
  • 您应该清理所有用户输入。总是。这意味着什么取决于您如何使用这些数据。您似乎在写入文本日志文件,因此您只想让可打印和空白类字符通过。进行防御性清理:不要指定坏字符代码并让其他所有内容通过,而是定义“好”字符的列表/类并让这些好字符通过。
  • 根据您的用例,您可能希望使用flock() 日志文件,以防止多个并行请求在您的文件中混合:

    $logtext = sanitizeLog($_POST[Message']); $fd = fopen("/path/to/log.txt", "a"); if(flock($fd, LOCK_EX)) { fseek($fd, 0, SEEK_END); fwrite($fd, $logtext); 羊群($fd,LOCK_UN);} fclose($fd);

我省略了对 fopen() 结果的检查......

于 2013-08-30T11:49:11.353 回答
0

关于 PHP 的 fwrite() 函数,不需要清理: fwrite() 只是将其写入它传递的文件。

关于日志文件,您可能希望清理. 原因如下:

假设攻击者将多行值作为消息发布。如果您的日志在帖子之前

line 1
line 2

然后在帖子之后

line 1 
line 2
line 3
remainder of line 3
very remainder of line 3

因为攻击者发布了这个:

line 3\nremainder of line 3\nvery remainder of line 3

注意:一次发布与添加 3 行。

也就是说:发布的数据需要如何清理,完全取决于您的应用程序。

于 2013-08-31T01:36:30.910 回答