見出し画像

ファイルアップロード時のウイルスチェック機能をサーバー(VPS)に導入する方法

本記事では、オープンソースのアンチウイルスソフトウェア「ClamAV(クラムエーブイ)」をxserver VPSにインストールし、サンプルサイトにウイルスチェック機能を実装する手順について解説します。

(注)この解説記事は、生成AIプログラミングの教科書『大蔵~TAIZO~』からの引用になります


ClamAVはオープンソースのアンチウイルスソフトウェアで、既知のウイルスを検出するために利用されます。以下の点に留意してください:

サポートするファイル形式:
画像ファイル、テキストファイル、音楽ファイル(MP3)など、様々なファイルのスキャンが可能です。

制限事項:
未知のマルウェア検出率は高くありません。また、誤検知(false positive)が発生する場合があります。
無料ツールのため、チェック機能への過度な期待は禁物です。


ClamAVのインストール手順


1.VPSへの接続

TERAterm などのツールを使用してシステム(VPS)に接続してください。


2.ClamAVのインストール

以下のコマンドでClamAVをインストールできます(Ubuntu/Debian 系の場合)。

sudo apt-get update
sudo apt-get install clamav


3.ウイルス定義ファイルの更新

ClamAV のデータベース(ウイルス定義ファイル)を最新にするため、以下の手順を実行します。

① freshclam プロセスを一旦停止

sudo systemctl stop clamav-freshclam.service

② データベースを更新

sudo freshclam

③ プロセスを有効化して再起動

sudo systemctl enable clamav-freshclam.service
sudo systemctl start clamav-freshclam.service


4.サービスの状態確認

ClamAV が正常に動作していることを確認するため、次のコマンドを実行します。

sudo systemctl status clamav-freshclam.service

・Loaded が enabled と表示されていれば有効化されています。
・Active が running と表示されていれば正常に動作中です。

これでClamAVのデータベースが、今後も自動的に更新されるようになりました。


アプリケーションディレクトリでの設定


1.アプリケーションディレクトリに移動

次は、VPS内でアップロード機能を実装しているアプリケーションのディレクトリに移動します。

cd /var/www/あなたのドメイン名


2.ClamScan npmライブラリのインストール

Node.js ベースのアプリケーションで ClamAV を利用する場合は、下記のコマンドでclamscanライブラリをインストールします。

npm install clamscan


これで、サーバーにアップロードされるファイルのウイルスチェックを行うための準備が整いました。

次はクライアント側とサーバー側に必要なコードを追加します。


アプリケーションのコード変更


1. クライアント側のコード修正


クライアント側のファイルに「ウイルスチェック実行中のメッセージ表示機能」「エラーハンドリング」のコードを追加し、Javascriptの中の「ファイルアップロード関連コード」を修正します。

① HTML  (サンプルサイトではdashboad.html)

アップロード中に「ウイルスチェック中」のオーバーレイを表示するため、クライアント側のhtmlファイルに下記のコードを追加します。

<!-- オーバーレイ表示 -->
<div id="virusCheckOverlay">
 <div class="overlay-text">ただいまウイルスチェック中です。しばらくお待ちください(約1015秒)。</div>
</div>

<body>タグ内ならどこでも問題ありませんが、<div id="toast"></div>と<footer>の間に記述すると良いでしょう。

② CSS  (サンプルサイトではdashboad.css)

オーバーレイ表示の装飾用CSSを、cssファイルに追加します。


        /* hiddenクラス:要素を非表示にする */
        .hidden {
            display: none;
        }

        /* オーバーレイの基本スタイル */
        #virusCheckOverlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(0, 0, 0, 0.5);
            color: white;
            font-size: 20px;
            text-align: center;
            padding-top: 40vh;
            z-index: 9999;
            visibility: hidden; /* 隠す */
            opacity: 0;
            transition: opacity 0.3s ease;
        }

        #virusCheckOverlay.show { /* ここで見える状態に */
            visibility: visible;
            opacity: 1;
        }

        /* アニメーションを当てたいテキストにクラスを付ける */
        .overlay-text {
            animation: flicker 1.5s infinite;
            /* 必要に応じて他のスタイルを追加 */
        }

        /* キーフレームで「揺らめき」を定義 */
        @keyframes flicker {

            0%,
            100% {
                opacity: 1;
                /* 不透明度 100% */
                transform: scale(1);
            }

            50% {
                opacity: 0.6;
                /* 半透明っぽく揺らめく */
                transform: scale(1.02);
                /* ほんの少し拡大したり */
            }
        }

HTMLとCSSを別ファイルに分けていない場合は、<style></style>タグで上記のCSSを囲い、それをhtmlファイルの<head>タグ内に記述してください。


③ JavaScript  (サンプルサイトではdashboad.js)

クライアント側 jsファイルのアップロードフォーム送信処理部分(サンプルサイトでは28行目~74行目)を、以下のコードに書き換えます。


    // ========= アップロードフォームの送信処理 =========
    document.getElementById('uploadForm').addEventListener('submit', function (event) {
        event.preventDefault();

        const overlay = document.getElementById('virusCheckOverlay');
        const uploadButton = this.querySelector('button[type="submit"]');
        const formData = new FormData(this);

        // 1) ファイル拡張子チェック
        const fileInput = this.querySelector('input[type="file"]');
        if (!fileInput.files || fileInput.files.length === 0) {
            alert('ファイルが選択されていません。');
            return;
        }
        const file = fileInput.files[0];
        const allowedExt = /\.(jpg|jpeg|png|webp)$/i;
        if (!allowedExt.test(file.name.toLowerCase())) {
            alert('許可されていないファイルタイプです。');
            return;
        }

        // 2) 他のバリデーション(画像名・キーワード)
        const imageName = formData.get('imageName');
        const keywords = formData.get('keywords');
        if (!validateInput(imageName, 200, '画像名') || !validateInput(keywords, 500, 'キーワード')) {
            return;
        }

        // 3) オーバーレイ表示 & 二重送信防止
        overlay.classList.add('show');
        uploadButton.disabled = true;

        // 4) 画像アップロードを実行し、同じスコープで then() チェーン
 fetch('/upload', {
    method: 'POST',
    headers: {
        'CSRF-Token': document.getElementById('csrfToken').value
    },
    body: formData
})
.then(response => {
    // 401 が返ってきた場合のハンドリング
    if (response.status === 401) {
        alert('セッションが切れています。ログインページに移動します。');
        window.location.href = '/login';
        // この return で .then(...) を終了
        return;
    }
    // 上記以外の場合は JSON を返す
    return response.json();
})
.then(data => {
    // data がない、または 401 のとき(上の return)には何もしない
    if (!data) return;

    // 成功か失敗かで分岐
    if (!data.success) {
        // エラー
        overlay.classList.remove('show');
        if (data.message) {
            alert(data.message);
        } else {
            alert('画像のアップロードに失敗しました。');
        }
        return;
    }

    // success = true
    alert('画像がアップロードされました!');
    overlay.classList.remove('show');
    fetchImages(1);
})
.catch(error => {
    console.error('Error:', error);
    alert('アップロード中にエラーが発生しました。');
    overlay.classList.remove('show');
    uploadButton.disabled = false;
});
});
});

ここでは、オーバーレイの表示・非表示を制御するためのコードを追加しています。


2. サーバー側  (サンプルサイトではuploadRoutes.js)のコード修正


① サーバー側ルート定義

サーバー側ファイルの冒頭部分(サンプルサイトではuploadRoutes.jsファイルの9行目あたり)に、下記のコードを追加してください。


const NodeClam = require('clamscan');

// 1) NodeClam の初期化は Promise を返すので、global変数に保持
let ClamScan = null;

// 2) 即時実行関数 (IIFE) で非同期初期化
(async () => {
  try {
    // clamdscanを使わずに、/usr/bin/clamscan コマンドだけでスキャン
    ClamScan = await new NodeClam().init({
      debugMode: false,     // 必要に応じてtrueにすると詳細ログが見られる
      preference: 'clamscan', // デフォルトでclamscanを使うようにする
      clamscan: {
        path: '/usr/bin/clamscan', // ClamAVがインストールされているパス
        db: null,                  // OSのデフォルトDBパスを使用 (例: /var/lib/clamav)
        active: true,             // clamscanを有効化
        scanArchives: true        // zipなどアーカイブのスキャンを有効化
      },
      clamdscan: {
        active: false,     // デーモンは使わない(メモリ節約)
      }
    });
    console.log('ClamAV (clamscan) is initialized successfully!');
  } catch (err) {
    console.error('Failed to initialize ClamScan:', err);
    // ここでプロセスを終了させるかどうかは運用次第
    // process.exit(1);
  }
})();

// エラーハンドリング関数
function handleError(res, status, message, error = null) {
  if (error) console.error(message, error);
  return res.status(status).json({ success: false, message });
}

ここでは、clamscan(ウイルススキャン)を使用するために必要な定義コードと、エラーハンドリング関数を追加しています。


② 画像アップロード用エンドポイントの修正

ここから先は

6,453字 / 2画像

¥ 2,000

この記事が気に入ったらチップで応援してみませんか?