[Node.js] ClamAVでウイルスチェック

  • このエントリーをはてなブックマークに追加
  • LINEで送る

Node.jsでウイルスチェックを行います。ユーザーがアップロードしたファイルを公開するようなサービスでは事前にチェックを行わないと、簡単にウイルスを撒き散らすスーパースプレッダーと化してしまいます。

今回は無料で使えるオープンソースのアンチウイルスソフト「ClamAV」を利用します。

- Sponsored Link -

最終的なコード

GitHubへアップしました。全体像が見たい方はこちらをどうぞ。

インストール

ClamAV

まずはClamAV本体を入れます。Amazon Linux2の場合は以下をご覧くださいませ。

macOSの場合はこちらをどうぞ。

インストールされたコマンドの場所や、ウイルスの定義がされたデータベースの場所をメモしておいてください。またデーモンを利用する場合には通信先のIPアドレス、ポート、socketファイルなどの情報が必要になります。

プロジェクトの準備

Node.jsをインストールしたら、npmなどでpackage.jsonを適当に用意します。今回はNode.jsからClamAVを利用できるclamscanモジュールをインストールします。

$ mkdir clamscan && cd clamscan
$ npm init
$ npm install clamscan

サンプルコード

ファイル単体をスキャン

NodeClam().init()で初期化、clamscan.is_infected(file)の引数にチェックしたいファイルのパスを渡すだけです。子プロセスが生成されclamscanコマンドが実行されます。

const path = require('path')
const NodeClam = require('clamscan')

// 初期化
const ClamScan = new NodeClam().init({
  clamscan: {
    path: '/usr/local/bin/clamscan',  // clamscanコマンドの絶対パス
    db: '/usr/local/var/lib/clamav'   // 定義ファイル用ディレクトリの絶対パス
  }
})

// ウイルススキャン
ClamScan
  .then(async clamscan => {
    try {
      const target = path.resolve('sample/eicar.com')
      const {is_infected, file, viruses} = await clamscan.is_infected(target)

      // 感染チェック
      if (is_infected){
        console.log(`${file} は ${viruses} に感染しています`)
      }
      else{
        console.log(`${file} は健康です`)
      }
    }
    catch (err) {
      console.eror(`[ERROR1] ${err}`)
    }
  })
  .catch(err => {
    console.error(`[ERROR2] ${err}`)
  });

ディレクトリをまるごとスキャン

ファイルをチェックするのとそれほど変わりません。NodeClam().init()で初期化、clamscan.scan_dir(dir, callback)でチェックを行います。こちらも子プロセスが生成されclamscanコマンドが実行されます。

const path = require('path')
const NodeClam = require('clamscan')

// 初期化
const ClamScan = new NodeClam().init({
  clamscan: {
    path: '/usr/local/bin/clamscan',  // clamscanコマンドの絶対パス
    db: '/usr/local/var/lib/clamav'   // 定義ファイル用ディレクトリの絶対パス
  }
})

// ウイルススキャン
ClamScan
  .then( clamscan => {
    try {
      const target = path.resolve('sample/')
      clamscan.scan_dir(target, (err, good_files, bad_files, viruses) => {
        // 実行時エラー
        if (err){
          console.log(err)
        }
        // 感染が見つかった
        else if (bad_files.length > 0) {
          console.log(`${viruses.join(',')}に感染しています`)
          console.log(bad_files.join('\n'))
        }
        // 感染なし
        else{
          console.log('健康です')
        }
      })
    }
    catch(err) {
      console.log(`[ERROR1] ${err}`)
    }
  })
  .catch(err => {
    console.log(`[ERROR2] ${err}`)
  })

デーモンを利用する

ここまでは通常のコマンドでしたが、デーモンを起動した状態でNodeClam().init()に通信先の情報をセットすることでデーモンを利用することができます。以下ではデーモンとTCPによる通信を行っています。

// 初期化
const ClamScan = new NodeClam().init({
  clamdscan: {
    socket: '/tmp/clamd.socket',
    host: '127.0.0.1',
    port: 3310,
    path: '/bin/clamdscan',
    config_file: '/usr/local/etc/clamav/clamd.conf'
  }
})

もちろんこちらの方が高速に動作します。頻繁に実行する場合はデーモンを利用したいところですが、メモリを1Gほど消費するので環境によっては注意が必要です。

その他

scan_dirをasync/awaitで利用するとエラーが発生する

当初はドキュメントに掲載されていた以下のコードを元に作成していたのですが、

const {path, is_infected, good_files, bad_files, viruses} = await clamscan.scan_dir('/some/path/to/scan');

以下のようなエラーが発生してしまいます。

$ node clamscan_dir.js
(node:76405) UnhandledPromiseRejectionWarning: Error: Error: spawn /usr/local/bin/clamscan --no-summary --stdout --remove=no --database=/usr/local/var/lib/clamav --scan-archive=yes -r sample sample/arupaka.png sample/eicar.com ENOENT

コードを追うのが面倒で原因がすぐには分からなかったので試しにCallbackのサンプルを利用したらこちらはエラーが発生せず、正常に動作しました。何でしょうね?

参考ページ

コメント

ご感想やご質問などお気軽にどうぞ。書き込むにはfacebookへのログインが必要です。

このブログを応援する

お寄せいただいたお気持ちは全額サーバ代や次の記事を執筆するための原資として活用させていただいております。この記事が参考になった場合などぜひご検討ください。

PayPal(ペイパル)
PayPalで300円支払う
※金額は任意で変更できます。
※100円でも泣いて喜びますw
※住所の入力欄が現れた場合は「no needed」を選択ください
これまでのご協力者さま
- Sponsored Link -