Node.jsでウイルスチェックを行います。ユーザーがアップロードしたファイルを公開するようなサービスでは事前にチェックを行わないと、簡単にウイルスを撒き散らすスーパースプレッダーと化してしまいます。
今回は無料で使えるオープンソースのアンチウイルスソフト「ClamAV」を利用します。
目次
最終的なコード
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のサンプルを利用したらこちらはエラーが発生せず、正常に動作しました。何でしょうね?
参考ページ
このブログを応援する
お寄せいただいたお気持ちは全額サーバ代や次の記事を執筆するための原資として活用させていただいております。この記事が参考になった場合などぜひご検討ください。
同じカテゴリの記事
- [Node.js] SequelizeでMySQLを利用する – その5「ToDoアプリ作成編」
- [Node.js] SequelizeでMySQLを利用する – その4「マイグレーション編」
- [Node.js] SequelizeでMySQLを利用する – その3「トランザクション編」
- [Node.js] SequelizeでMySQLを利用する – その2「SELECT文編」
- [Node.js] SequelizeでMySQLを利用する – その1「チュートリアル編」
- [JavaScript] クレジットカード番号から国際ブランドを特定する
- [JavaScript] クレジットカード番号が正しいか検証する
- [Node.js] 指定した時間sleepする – Promise版