php 使用 clamav 进行文件上传扫描,/tmp/ 上的权限

php file upload scanning using clamav, permissions on /tmp/

我正在尝试使用 clamav 在 apache 2.4、php 5.6 上使用 clamav 守护程序的套接字扫描文件(通常为 100MB 以上的 zip)。我没有使用 PHP-FPM。 (p.s。套接字工作,我可以发送 PING 并获得 PONG)。

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

if (isset($_FILES['file']) && is_uploaded_file($_FILES['file']['tmp_name'])) {

    $socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
    if(socket_connect($socket, '/var/run/clamav/clamd.ctl')) {
        $result = "";
        $file = $_FILES['file']['tmp_name'];
        socket_send($socket, "SCAN $file", strlen($file) + 5, 0);
        socket_recv($socket, $result, 20000, 0);
        var_dump($result);
    }
    socket_close($socket);
}
?>

<form method="post" enctype="multipart/form-data"><input type="file" name="file"><input type="submit"></form>

上传文件产生:

string(65) "/tmp/phpxYBjyS: lstat() failed: No such file or directory. ERROR "

这在我看来像是一个权限错误(即使 /tmp 是 drwxrwxrwt 9 root root 4096 Dec 4 13:10 tmp)。但我可能是错的。我无法轻易查看文件,因为在 php 进程完成后文件被删除。

clamdscan,在命令行中,当然有效(例如 /tmp/virus.txt: Eicar-Test-Signature FOUND)。

我的/etc/clamav/clamd.conf还是默认的。它看起来像这样:

LocalSocket /var/run/clamav/clamd.ctl
FixStaleSocket true
LocalSocketGroup clamav
LocalSocketMode 666
# TemporaryDirectory is not set to its default /tmp here to make overriding
# the default with environment variables TMPDIR/TMP/TEMP possible
User clamav
AllowSupplementaryGroups true
ScanMail true
ScanArchive true
ArchiveBlockEncrypted false
MaxDirectoryRecursion 15
FollowDirectorySymlinks false
FollowFileSymlinks false
ReadTimeout 180
MaxThreads 12
MaxConnectionQueueLength 15
LogSyslog false
LogRotate true
LogFacility LOG_LOCAL6
LogClean false
LogVerbose false
DatabaseDirectory /var/lib/clamav
OfficialDatabaseOnly false
SelfCheck 3600
Foreground false
Debug false
ScanPE true
MaxEmbeddedPE 10M
ScanOLE2 true
ScanPDF true
ScanHTML true
MaxHTMLNormalize 10M
MaxHTMLNoTags 2M
MaxScriptNormalize 5M
MaxZipTypeRcg 1M
ScanSWF true
DetectBrokenExecutables false
ExitOnOOM false
LeaveTemporaryFiles false
AlgorithmicDetection true
ScanELF true
IdleTimeout 30
CrossFilesystems true
PhishingSignatures true
PhishingScanURLs true
PhishingAlwaysBlockSSLMismatch false
PhishingAlwaysBlockCloak false
PartitionIntersection false
DetectPUA false
ScanPartialMessages false
HeuristicScanPrecedence false
StructuredDataDetection false
CommandReadTimeout 5
SendBufTimeout 200
MaxQueue 100
ExtendedDetectionInfo true
OLE2BlockMacros false
ScanOnAccess false
AllowAllMatchScan true
ForceToDisk false
DisableCertCheck false
DisableCache false
MaxScanSize 100M
MaxFileSize 25M
MaxRecursion 16
MaxFiles 10000
MaxPartitions 50
MaxIconsPE 100
PCREMatchLimit 10000
PCRERecMatchLimit 5000
PCREMaxFileSize 25M
ScanXMLDOCS true
ScanHWP3 true
MaxRecHWP3 16
StatsEnabled false
StatsPEDisabled true
StatsHostID auto
StatsTimeout 10
StreamMaxLength 25M
LogFile /var/log/clamav/clamav.log
LogTime true
LogFileUnlock false
LogFileMaxSize 0
Bytecode true
BytecodeSecurity TrustSigned
BytecodeTimeout 60000

/编辑 尝试使用 exec 而不是套接字。

if (isset($_FILES['file']) && is_uploaded_file($_FILES['file']['tmp_name'])) {
  $path = escapeshellarg($_FILES['file']['tmp_name']);
  $code = -1;
  $result = '';
  exec('clamdscan ' . $path, $result, $code);
  if ($code !== 0) {
    var_dump($result);
  }
}

这也会产生类似的错误

array(6) {
[0]=>
string(64) "/tmp/php2hQTE8: lstat() failed: No such file or directory. ERROR"
[1]=>
string(0) ""
[2]=>
string(36) "----------- SCAN SUMMARY -----------"
[3]=>
string(17) "Infected files: 0"
[4]=>
string(15) "Total errors: 1"
[5]=>
string(25) "Time: 0.000 sec (0 m 0 s)"
}

这看起来 Moodle 运行 在使用 systemd PrivateTmp 的服务下。

许多发行版现在为 apache 和 php-fpm 等服务配置 systemd 单元,为服务提供自己的私有 tmp 目录(至少部分是出于安全原因)。

如果您在 systemd 单元文件中查找 apache 或 php-fpm(在 /lib/systemd/system 下查找名为 apache2.servicehttpd.servicephp-fpm.service),您可能会在 [Service] 部分看到行 PrivateTmp=true

覆盖它的最佳方法(如果您决定需要这样做)是在服务的相关 /etc/systemd/systemd/ 子目录中创建一个覆盖文件。您可以对相关服务使用 systemctl edit 来执行此操作,例如systemctl edit php-fpm.service if 运行 在 RHEL 上的 php-fpm 下。 apache2 的服务名称会有所不同,具体取决于您使用的发行版。

覆盖文件应仅包含:

# A comment to explain why you're doing this
[Service]
PrivateTmp=false

您可能需要将用户 clamd is 运行 添加到 webserver 组,以授予它访问临时文件的权限实际上看到它,例如usermod -a -G apache clamscan 在 RHEL 上。

尝试将 --fdpass 作为选项传递给 clamdscan。

我有一个类似的问题,文件可以用 clamscan 找到,但不能用 clamdscan 找到,它对我有用。

--fdpass Pass the file descriptor permissions to clamd. This is useful if clamd is running as a different user as it is faster than streaming the file to clamd. Only available if connected to clamd via local(unix) socket.

来源:https://linux.die.net/man/1/clamdscan

运行 clamdscan $the_file 不一定有效,因为 Apache 的 systemd-private-tmp。 你会收到日志消息 “文件路径检查失败:没有这样的文件或目录。”

但简单的方法是: cat $the_file | clamdscan - 或者,使用 --stream 选项(虽然我发现 --fdpass 选项没有帮助)。