[それweb] アップロードされたファイルのウイルススキャンをリモートのClamAVにやらせる [でやるの？]

皆さんこんにちは。

皆さん、ウイルスチェックしてますか？
いや、自分のローカルとかじゃなくて、システム上で、アップロードされたファイルに対して、ですぜ？

そんな必要あるの？って思うかもしれませんが、某R社系列の案件であれば、普通に「やってね♡」って言ってきますし、ファイルのやり取りを頻繁に行うサービスであれば、ウイルスチェックをしないといつ脅威が紛れ込むとも限りませんし、そこから情報流出なんて事態になれば、一発で会社が潰れてもおかしくないわけで、備えておく分にはやはり有用なのではないかと思ったりするわけです。

というわけで、PHPでリモートのClamAVを使ったウイルスチェックをやっていきましょう。

# 3行で締める
- ウイルスチェックサーバをWebサーバから切り離して運用したい
- リモートのウイルスチェックサーバにTCPで通信して、ファイルのスキャンしてもらうためのPHPライブラリを作ったのだ
- これでアップロードファイルを気軽にウイルスチェックできますね！

まあ、そんな話です

# ウイルスチェックの導入
## ClamAV
オープンソースのアンチウイルスソフトです。https://www.clamav.net/
Linuxで使えます。

## Webサーバでウイルスチェック

以前の案件ではWebサーバの中にPHPだけでなく、ウイルスチェックソフトのClamAVも入れていました。ただ、ClamAV自体は起動が遅いので、clamdというdaemonを使っていたのですが、これがとても重い。メモリをドカ食いするので、コンテナで下手にメモリ上限を入れて置こうものなら、すぐに落ちるくらい重いです。
これをWebサーバの台数だけ用意するのですから、余計なリソースがかかってしまい、経済的ではなかったのです。

![text10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/0fed031d-2e64-7f1a-49a6-7edca4139c67.png)

該当案件の方には本当に申し訳ない。

## ウイルスチェックの切り離し

ウイルスチェックの機構を各Webサーバに突っ込むからいけないのであって、こいつを分離してしまえば諸々解決です。

![text10-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/e69ce444-8a9b-64e8-4139-54d83fea5c04.png)

こうしておけば、WebサーバはWebサーバの仕事のみをしていればよいわけですし、ウイルスチェックのサーバも一つあれば足りるので経済的です。
こっちの形式で実装することを考えましょう。

## TCPでClamAVにファイルを送ってウイルスチェックするライブラリ
なんかいいのがなかったので作りました。

https://github.com/niisan-tokyo/web-clamav-php

説明は別記事で書きます。
あるものが使えればいいんです。

書くの忘れてたけど、多分`php > 7` で動くと思います。

# 実装
簡単にアップロードしたファイルをウイルスチェックして、その結果を返すクソアプリを作ります。

## 環境
みんな大好き`docker-compose`で作りますよ。

```docker-compose.yml
version: "3"

services:
    workspace:
        build: workspace
        command: sleep infinity
        volumes:
            - ../:/var/www/
        environment:
            - LANGUAGE=en_US.UTF-8
            - LC_ALL=en_US.UTF-8
        ports:
            - 8888:8888

    clamav:
        image: dinkel/clamavd
        ports:
            - 3310
```
clamavはdockerhubに落ちてるTCPアクセスできるclamavサーバです。これで状況的には以下の先に挙げた図の状態を再現できます。
![text10-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/e69ce444-8a9b-64e8-4139-54d83fea5c04.png)
workspaceは以下のDockerfileで作られるコンテナで、普通にcomposer入れただけのやつです。

```Dockerfile
FROM php:7.4

RUN apt-get update && apt-get install -y unzip

COPY --from=composer /usr/bin/composer /usr/bin/composer
```
あとは`docker-compose up -d` でコンテナを立ち上げます。
(私の場合はVS Code Remote Container使ってます。)

## アプリを作る
まず、workspace内でライブラリをダウンロードします。

```sh
composer init
composer require niisan-tokyo/web-clamav-php
```
とりあえず、これで必要なライブラリは入ったはずです。

次にフォームを作ります。

```index.html
<!DOCTYPE html>
<head>
    <meta charset="utf-8" />
</head>
<body>
    <h1>ファイルを送信</h1>
    <form action="file.php" enctype="multipart/form-data" method="POST">
        <input type="file" name="upfile" /><br>
        <button type="submit" name="go">送信する</button>
    </form>
</body>
```
最後にアップロードされたファイルをウイルススキャンするページを作ります。

```file.php
<?php
require 'vendor/autoload.php';
use Niisan\ClamAV\Manager;

$file = $_FILES['upfile'];

$scanner = new Manager(['url' => 'clamav']);// clamav のあるサーバのurlを指定。IPでも多分大丈夫
$result = $scanner->scan($file['tmp_name']);// 対象ファイルのパスを指定してスキャン

?>
<!DOCTYPE html>
<head>
    <meta charset="utf-8" />
</head>
<body>
<h1>問題<?php if ($result) {?> なし <?php } else {?> あり <?php } ?>！</h1>
    <a href="index.html">戻る</a>
</body>
```
とりあえず、こんな感じです。
ClamAVの動くサーバさえ用意できれば、実装は楽勝ですね。

## 動作確認
では、PHPのビルトインサーバで動作確認しましょう。

```
php -S 0.0.0.0:8888
```
まずはフォームにアクセスします。
http://localhost:8888/

<img width="198" alt="webclamindex.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/18303b54-6a4b-a128-d25c-6ec6824dbb04.png">

次に、フォームにファイルを突っ込んで送信します。

<img width="218" alt="webclamfile.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102182/9a5f4b52-1563-ff64-378b-0908e6ea619b.png">

よさそう。

# まとめ
というわけで、リモートのClamAVを使ってファイルのウイルスチェックする機構を実装しました。
今回詳しく説明しなかったライブラリの作成のほうは割と苦労したのですが、サクッと使いたい人にとってはどうでもいいと思ったので、今回は入れませんでした。
ライブラリ除いたら、実装時間は5分くらいしかかからないので、もしも「あ、アップロードされたファイルのウイルスチェックもよろしくね♡」って言われたときは、この記事を参考にしていただけると幸いです。

今回はこんなところです。