查看: 135|回复: 1

Node.js文件上传集成ClamAV扫描:pompelmi库落盘前检测恶意文件

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
文件上传是Web应用常见的安全攻击面,用户上传的文件可能包含恶意软件、ZIP炸弹或伪造MIME类型。大多数Node.js项目仅做文件扩展名检查,远远不够。pompelmi是一个Node.js库,它能在文件落盘之前完成ClamAV扫描,返回类型化的verdict symbol,不依赖第三方运行时。

工作原理

pompelmi通过Node.js内置的child_process调用clamscan,直接读取退出码并将其映射为Symbol类型的结果。没有stdout解析,没有正则匹配,没有隐式状态,干净利落。

安装依赖

首先安装Node.js库:
  1. npm install pompelmi
复制代码

然后在操作系统上安装ClamAV:
- macOS:
  1. brew install clamav && freshclam
复制代码
- Debian/Ubuntu:
  1. sudo apt-get install -y clamav clamav-daemon && sudo freshclam
复制代码
- Windows:
  1. choco install clamav -y
复制代码

基本用法

导入模块并扫描文件:
  1. const { scan, Verdict } = require('pompelmi');
  2. const result = await scan('/path/to/file.zip');
  3. switch (result) {
  4.   case Verdict.Clean:
  5.     // 文件安全,继续处理
  6.     break;
  7.   case Verdict.Malicious:
  8.     throw new Error('检测到恶意软件,文件已拒绝');
  9.   case Verdict.ScanError:
  10.     // 扫描未完成,按不可信文件处理
  11.     console.warn('扫描失败,拒绝文件');
  12.     break;
  13. }
复制代码

返回值映射关系:
- Verdict.Clean:ClamAV退出码0,未发现威胁
- Verdict.Malicious:退出码1,匹配到已知病毒签名
- Verdict.ScanError:退出码2,扫描本身失败,文件状态未知

在Express中集成

使用multer处理文件上传,在保存前调用pompelmi扫描:
  1. const express = require('express');
  2. const multer = require('multer');
  3. const { scan, Verdict } = require('pompelmi');
  4. const path = require('path');
  5. const fs = require('fs');
  6. const app = express();
  7. const upload = multer({ dest: 'tmp/' });
  8. app.post('/upload', upload.single('file'), async (req, res) => {
  9.   const filePath = path.resolve(req.file.path);
  10.   try {
  11.     const result = await scan(filePath);
  12.     if (result === Verdict.Malicious) {
  13.       fs.unlinkSync(filePath);
  14.       return res.status(422).json({ error: '文件包含恶意软件' });
  15.     }
  16.     if (result === Verdict.ScanError) {
  17.       fs.unlinkSync(filePath);
  18.       return res.status(422).json({ error: '扫描失败,文件已拒绝' });
  19.     }
  20.     // Verdict.Clean — 继续保存文件
  21.     return res.status(200).json({ verdict: 'clean' });
  22.   } catch (err) {
  23.     fs.unlinkSync(filePath);
  24.     return res.status(500).json({ error: err.message });
  25.   }
  26. });
复制代码

扫描失败或检测到恶意文件时,立即删除临时文件并返回422状态码。

远程扫描(Docker环境)

当ClamAV运行在容器中时,通过TCP socket连接:
  1. const result = await scan('/path/to/file.zip', {
  2.   host: '127.0.0.1',
  3.   port: 3310,
  4. });
复制代码
API保持不变,verdict类型不变,无缝切换。

错误处理

scan函数可能抛出以下异常:
- filePath不是字符串 → 'filePath must be a string'
- 文件不存在 → 'File not found: <path>'
- clamscan不在PATH中 → ENOENT
- 未知退出码 → 'Unexpected exit code: N'

建议在try/catch中处理:
  1. try {
  2.   const result = await scan(path.resolve(filePath));
  3.   return result;
  4. } catch (err) {
  5.   console.error('扫描异常:', err.message);
  6.   return null;
  7. }
复制代码

特性总结

- 零运行时依赖,仅使用Node.js内置child_process
- 不解析stdout,直接读取退出码,性能可靠
- 支持TypeScript,verdict为Symbol类型,防止拼写错误
- 支持本地clamscan和远程clamd TCP socket
- 跨平台:macOS、Linux、Windows

通过pompelmi库,开发者可以用最少的代码在文件上传流程中集成ClamAV病毒扫描,提升应用安全性,避免用户上传恶意文件造成危害。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Node.js文件上传集成ClamAV扫描:pompelmi库落盘前检测恶意文件

这个方案很实用,特别是“落盘前扫描”的思路,比事后扫描安全不少。pompelmi 直接读退出码不解析 stdout,确实干净,避免了正则解析带来的各种坑。 有个实际场景想请教一下:当并发上传请求较多时,每次调用 `child_process` 执行 `clamscan`,会不会有性能瓶颈?比如频繁 fork 进程导致 CPU 飙升,或者有没有内置的连接池机制来复用远程 clamd 的 TCP 连接? 另外,`freshclam` 的更新策略通常怎么配合比较合理?如果用户上传文件时候病毒库还是旧的,可能会漏报。建议在文档里加一段定时更新数据库的示例,或者检查 `clamscan --version` 的数据库日期。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-12 23:07 , Processed in 0.027603 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部