【问题标题】:How to prevent mcrypt_encrypt() from failing to encrypt larger file?如何防止 mcrypt_encrypt() 无法加密较大的文件?
【发布时间】:2015-01-30 07:07:19
【问题描述】:

我正在尝试使用类似于this one 的 PHP 脚本加密上传的文件。它适用于较小的文件,但是当我尝试上传大小为 49.2 MB 的测试文件(按照今天的标准并没有那么大)时,以下会导致我的 php 页面通过终止脚本显示一个空白页面:

$binaryFileData = file_get_contents($serverFilePath);
$binaryEncFile = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $binaryKey, $binaryFileData, MCRYPT_MODE_CBC, $binaryIV);

我在error_log 文件中得到一行:

PHP 致命错误:允许的内存大小为 134217728 字节已用尽 (试图分配 51694736 字节)在 /home2/myaccount/public_html/myfldr/myfile.inc 在第 620 行

在我上面的示例中,第 620 行指向 mcrypt_encrypt

所以我做了一些研究,人们建议将以下内容添加到 php.ini 文件中:

memory_limit = -1

我做了,但结果还是一样。

所以我有一个两部分的问题:

  1. 显然,如何防止该异常终止我的脚本?

  2. 有什么方法可以让该函数返回错误,或者抛出我可以捕获的异常,而不是仅仅终止(并导致向用户显示白色/空白页面?)

如果加密失败,我还需要解决上面的第 2 项,以便能够从服务器上删除未加密的文件(如果 mcrypt_encrypt 只是终止我的脚本,我显然不能这样做。

PS。我需要说我在 BlueHost 托管的共享帐户上运行此脚本。

【问题讨论】:

  • 您的 ISP 可能已禁用更改内存限制以在多个客户端之间共享资源。也许您需要支付更多费用。
  • @HoboSapiens:嗯。或许。但如果是这种情况,mcrypt_encrypt 函数就有点没用了,不是吗?另外,我很好奇,因为将整个文件加载到 RAM 中并像这样加密它实际上可能是一个坏主意,有没有办法逐块进行?或者换句话说,通过读取、加密和写入文件的小块,一次一个。

标签: php encryption error-log php-ini


【解决方案1】:

使用

ini_set(“memory_limit”,”640M“);

将内存限制设置为更高的限制。 以上将限制从 128MB 增加到 640Mb。

如果仍然遇到问题,请使用

realpath_cache_size = 16k
realpath_cache_ttl = 120

请注意,在 php 5.3 及之后的版本中,您可以简单地将 user.ini 文件(行 memory_limit = 640M)放在 public_html 目录中,但并非所有 cpanels 都允许这样做。

【讨论】:

  • 谢谢。只是好奇,为什么通过ini_set 设置它有效果,而通过php.ini 更改它没有效果?
  • 某些设置会忽略(不幸的是).ini 文件设置。今天的趋势是尽可能地支持它们,因为通用配置可以通过 .inis 非常容易地编写。
  • 我明白了。我的第二个问题呢——如果我超过限制,有没有办法阻止该函数终止脚本?
  • 我认为您可以在终止发生之前暂停它。您可以使用 mem-size 相关函数来检测占用的内存何时接近分配的内存并在达到限制之前使用暂停。
  • 嗯。知道该怎么做吗?
【解决方案2】:

一种可能的解决方法是将文件加密为小块(例如一次 4128 个字节)。

示例代码:

$fr=fopen('input.file','r');
$fw=fopen('output.file','w');
while(!feof($fr))
{
    $buffer=fread($fr,4128);
    $result=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $binaryKey, $buffer, MCRYPT_MODE_CBC, $binaryIV);
    false!==$result && fwrite($fw,$result);
}
fclose($fw);
fclose($fr);

这样您最终会得到一个加密文件,稍后可以通过反转这些步骤来解密该文件。

请注意,块大小 4128 它不是随机选择的。知道 RIJNDAEL 支持 128/192/256 位的块(即 16/24/32 字节),我确定了这些数字(即 96)之间的 LCM,然后我选择了任何 96 的倍数的块大小(大约 4K) . 正好4128符合条件。

为什么块大小很重要?因为 MCrypt 将您的输入文本划分为您的块大小长度的固定块,如果有提醒,那么它会用零填充该空间,直到块长度完全划分。所以我们不想让 MCrypt 这样做,对吧?无论如何,请记住,它会对文件的最后一个块执行此操作(除非您足够幸运,以至于它已经是您的块大小的倍数。

希望对你有帮助……

【讨论】:

  • 我不太确定 4128 字节或任何硬编码值,但这是我最终使用的方法,即分块加密。需要注意的重要一点是,每个加密块的大小必须是密码块大小的倍数。可以通过调用mcrypt_get_iv_size 获得它,例如:$cipher_block_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
猜你喜欢
  • 1970-01-01
  • 2015-03-13
  • 2011-12-28
  • 1970-01-01
  • 1970-01-01
  • 2015-09-16
  • 2018-03-15
  • 2022-11-10
  • 2023-03-27
相关资源
最近更新 更多