2021年10月29日

プログラミング

clambyを利用したウイルススキャン

目次

  1. はじめに
  2. clamavの導入
  3. gem clambyの導入
  4. ウイルススキャンの処理を追加
  5. beanstalkへのclamavの導入
  6. 擬似ウイルスファイルでの挙動確認
  7. ウイルス定義ファイルの更新
  8. まとめ

はじめに

Railsでアップロードファイルに対してウイルススキャンを行う方法をご紹介したいと思います。

今回はgem clambyを利用して、ファイルアップロード後に非同期処理でウイルススキャンを実行。

ウイルスが検出された場合はダミーファイルを差し替えるという流れです。

clamby のGithubリポジトリ

今回使用する環境

  • Ruby: 2.7.4
  • Ruby on Rails: 6.1.4
  • clamby: 1.6.6

clamavの導入

clamav のインストール

gem clambyはclamavに依存しているため、まずは clamav をインストールします。

mac OS(homebrew利用)の場合

$ brew install clamav

Dockerを利用している場合

Dockerfile


RUN apt-get -y install clamav clamav-daemon && freshclam

gem clambyの導入

gem clamby のインストール

Gemfile


gem 'clamby'

$ bundle install を実行

ウイルススキャンの処理を追加

アップローダーをマウントしているモデルにafter_saveを追加

user_image.rb


class UserImage < ApplicationRecord
  attr_accessor :skip_scan_viruses
  after_save -> { scan_viruses(:image) }, unless: :skip_scan_viruses
  ...
  mount_uploader :image, ImageUploader

  private

  def scan_viruses(attribute)
    ScanVirusesJob.perform_later(self, attribute)
  end
end

ウイルススキャンをスキップすることを可能にするためattr_accessorにskip_scan_virusesを追加

ウイルススキャンのジョブを作成

syoryukenを利用していますが非同期処理の方法についてはスキップします。
ウイルススキャンを行い、検出された場合はダミーファイルと差し替えの上メールを送信します。

scan_viruses._job.rb


class ScanVirusesJob < ApplicationJob
  def perform(record, attribute)
    return if record.send(attribute).blank? || Rails.env.test?
    return if Clamby.safe?(file_path(record, attribute))
    record.send("#{attribute}=", File.open('app/assets/images/common/no_image.png'))
    # 差し替え済みファイルなのでウイルススキャンはスキップ
    record.skip_scan_viruses = true
    record.save!(validate: false)
    Admin::ScanVirusesMailer.virus_detected_email(record).deliver_now
    if Rails.env.production?
      SlackAlertNotification.call(title: "#{notify_env}: ウイルスを検出しました", text: slack_text(record))
    end
  end

  private

  def file_path(record, attribute)
    # ファイルのpathを渡さなければいけないのでs3に保存しているproductionとそれ以外では分岐
    if Rails.env.production?
      # s3のファイルが上手く開ける状態かの確認
      # carrierwaveの挙動の問題か、s3側の準備ができておらずnot foundとなってしまうことがあった
      # sleep 2を噛ませたら上手く動いたので、200を確認してからウイルススキャンするよう修正
      5.times do
        response = Net::HTTP.get_response(URI.parse(record.send(attribute).url))
        break if response.code == '200'
        sleep 1
      end
      # URI.open(record.send(attribute).url).pathをそのままpathとして渡していた
      # が、疑似ウイルスファイルでundefined method `path' for #発生
      # 原因は10kb以下のファイルは Tempfile ではなく StringIOを返すRubyの仕様
      # なので、強制的にTempfileを作成する
      temp_file = Tempfile.new('file_')
      temp_file.binmode
      URI.open(record.send(attribute).url) { |file| temp_file.write(file.read) }
      temp_file.rewind
      temp_file.path
    else
      record.send(attribute).path
    end
  end
end

Point!

  • ウイルスを検出してファイル差し替え後はウイルススキャンをスキップ
  • S3のファイルが開ける状態かの確認
  • 強制的にTempfileを作成する

挙動確認

擬似ウイルスファイルでの挙動確認

EICARのサイトで実害のないテスト用の擬似ウイルスファイルをダウンロードできます。
ダウンロードしたファイルをアップロードしてファイルが差し替えられることを確認します。

EICARテストファイル(wikipedia)

beanstalkへのclamavの導入

ebextensionsを利用してbeanstalkにclamavをインストール

今回はAWSのbeanstalkというサービスを利用しているためebextentionsディレクトリを利用してclamavのインストールと定義ファイルの更新を行います。

.ebextensions/01_settings.config


  03_install_clamav:
    command: "sudo amazon-linux-extras install -y epel && sudo yum install -y clamav clamav-update clamd"

  04_freshcram:
    command: "sudo freshclam" # ウイルス定義ファイルの更新

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

cronを利用して定義ファイルの更新を更新を定時実行する

gem wheneverを利用しているためschedule.rbに追記します。

config/schedule.rb


every 1.day, at: '0:30 am' do
  command "sudo freshclam"
end

まとめ

gem clamavを利用したウイルススキャンの実装をご紹介しました。
あまりウイルススキャンに関する記事は多くないので実装に少し苦労しましたが、こちらの記事が何かの助けになれば幸いです!

Rubyのお仕事に関するご相談

Bageleeの運営会社、palanではRubyに関するお仕事のご相談を無料で承っております。
zoomなどのオンラインミーティング、お電話、貴社への訪問、いずれも可能です。
ぜひお気軽にご相談ください。

無料相談フォームへ

1

0

AUTHOR

はらた

はらた エンジニア

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

アプリでもっと便利に!気になる記事をチェック!

記事のお気に入り登録やランキングが表示される昨日に対応!毎日の情報収集や調べ物にもっと身近なメディアになりました。

簡単に自分で作れるWebAR

「palanAR」はオンラインで簡単に作れるWebAR作成ツールです。WebARとはアプリを使用せずに、Webサイト上でARを体験できる新しい技術です。

palanARへ
palanar

palanはWebARの開発を
行っています

弊社では企画からサービスの公開終了まで一緒に関わらせていただきます。 企画からシステム開発、3DCG、デザインまで一貫して承ります。

webar_waterpark

palanでは一緒に働く仲間を募集しています

正社員や業務委託、アルバイトやインターンなど雇用形態にこだわらず、
ベテランの方から業界未経験の方まで様々なかたのお力をお借りしたいと考えております。

話を聞いてみたい

運営メンバー

eishis

Eishi Saito 総務

SIerやスタートアップ、フリーランスを経て2016年11月にpalan(旧eishis)を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。

sasakki デザイナー

アメリカの大学を卒業後、日本、シンガポールでデザイナーとして活動。

yamakawa

やまかわたかし デザイナー

フロントエンドデザイナー。デザインからHTML / CSS、JSの実装を担当しています。最近はReactやReact Nativeをよく触っています。

Sayaka Osanai デザイナー

Sketchだいすきプロダクトデザイナー。シンプルだけどちょっとかわいいデザインが得意。 好きな食べものは生ハムとお寿司とカレーです。

はらた

はらた エンジニア

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

kobori

こぼり ともろう エンジニア

サーバーサイドエンジニア。SIerを経て2019年7月に入社。日々学習しながらRuby on Railsを使った開発を行っています。

sasai

ささい エンジニア

フロントエンドエンジニア WebGLとReactが強みと言えるように頑張ってます。

damien

Damien

WebAR/VRの企画・開発をやっています。森に住んでいます。

ゲスト bagelee

ゲスト bagelee

かっきー

かっきー

まりな

まりな

suzuki

suzuki

miyagi

ogawa

ogawa

雑食デザイナー。UI/UXデザインやコーディング、時々フロントエンドやってます。最近はARも。

いわもと

いわもと

デザイナーをしています。 好きな食べ物はラーメンです。

kobari

taishi kobari

フロントエンドの開発を主に担当してます。Blitz.js好きです。

shogokubota

kubota shogo

サーバーサイドエンジニア。Ruby on Railsを使った開発を行いつつ月500kmほど走っています!

nishi tomoya

aihara

aihara

グラフィックデザイナーから、フロントエンドエンジニアになりました。最近はWebAR/VRの開発や、Blender、Unityを触っています。モノづくりとワンコが好きです。

nagao

SIerを経てアプリのエンジニアに。xR業界に興味があり、unityを使って開発をしたりしています。

kainuma

Kainuma

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

sugimoto

sugimoto

asama

ando

iwasawa ayane

oshimo

yoko oshimo

異業界からやってきたデザイナー。palanARのUIをメインに担当してます。これからたくさん吸収していきます!

CONTACT PAGE TOP