[rails]carrierwaveでファイル内容をもとにcontent-type判定を行う


ruby2.1.4, rails4.1.7で確認。

carrierwaveでファイルアップロード機能を作成した場合、extension_white_listメソッドを使えば拡張子によるcontent-type判定ができます。

しかし、extension_white_listだけだと拡張子を偽装したファイルはアップロード可能になってしまいます。

そのため、厳密にチェックしたい場合はファイルの内容をもとにcontent-typeを判定する必要があります。

今回、「CarrierWaveでファイルの内容をもとにcontent-typeの判定を行う」を参考にさせていただきました。

1. carrierwave-magic gemをインストール

carrierwave-magic gemを入れることでファイル内容をもとにcontent-type判定が可能になります。

# Gemfile

gem 'carrierwave-magic'

なお、carrierwave-magicを使用する場合は以下のライブラリをインストールしておく必要があります。

sudo yum install file file-devel

上記ライブラリをインストールしていない場合は、以下のエラーが発生します。

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

/home/vagrant/.rbenv/versions/2.1.4/bin/ruby extconf.rb
checking for magic_open() in -lmagic... no
*** ERROR: missing required library to compile this module
*** extconf.rb failed ***

2. carrierwave設定

今回は app/uploaders/sample_uploader.rb をアップローダーとして説明します。

class SampleUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick
  include CarrierWave::Magic                # これを追加
  process :set_magic_content_type => [true] # これを追加

  process :fix_exif_rotation

  # for mobile upload
  def fix_exif_rotation
    manipulate! do |img|
      img.auto_orient!
      img = yield(img) if block_given?
      img
    end
  end
end

set_magic_content_typeでcontent-typeのチェックを行っています。

fix_exif_rotationは今回のサンプルとはあまり関係ないですが、iOS端末での写真向きの自動最適化を解除するようにしています。

iPhoneで撮影した写真が横向きになってしまう場合のcarrierwaveでの対処法」に詳しい解説がありました。

iOS端末から写真アップロードを行う場合は設定しておきましょう。

3. modelにvalidation追加

content-type判定用のカスタムバリデータを作ります。

lib/autoload/sample_image_content_validator.rb を追加します。

# lib/autoload/sample_image_content_validator.rb

class SampleImageContentValidator < ActiveModel::EachValidator
  def initialize(options)
    options.reverse_merge!(:message => :invalid)
    super(options)
  end

  def validate_each(record, attribute, value)
    return if value.nil?
    white_list_content_types = ["image/jpg", "image/jpeg", "image/png", "image/x-citrix-png", "image/x-citrix-jpeg", "image/x-png", "image/pjpeg"]
    unless white_list_content_types.include?(value.file.content_type)
      record.errors.add(attribute, options[:message])
    end
  end
end

今回は、jpg, jpeg, pngを許可する設定になっています。

x-citrix-png, x-citrix-jpeg, x-png, pjpegはIEの場合にjpeg、pngのMIME typeが異なるので、その対応策です。

なお、lib/autoload以下が自動読み込みになっていない場合は、application.rbで設定をしておいてください。

#config/application.rb

config.autoload_paths += %W(#{config.root}/lib/autoload)

最後にモデル側でvalidation設定をします。

# app/models/hoge.rb

class Hoge < ActiveRecord::Base
  mount_uploader :sample_image, SampleUploader

  validates :sample_image,
    presence: true,
    sample_image_content: true # content-typeチェックを有効に

これでファイル内容をもとにcontent-type判定ができるようになりました。

pdf等、許可していない拡張子をpngに変更してアップロードを行うとバリデーションエラーになります。

参考