[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に変更してアップロードを行うとバリデーションエラーになります。